tag:blogger.com,1999:blog-92368222016-02-12T00:05:48.489-05:00YooperGeekGeekery, Homebrew...and Whatever Else Comes to MindJason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.comBlogger62125tag:blogger.com,1999:blog-9236822.post-76765088198140137702011-03-10T10:21:00.001-05:002011-04-13T14:49:34.732-04:00Windows 7 SP1 Introduces Breaking Changes to MDACWow, this one really took our office by surprise. A few of us here have updated to Windows 7 SP1. Some of the developers are also charged with maintaining some legacy VB6 applications that used ADODB to work with MS SQL Server databases. <br /><br />If you compile this legacy code on Win7SP1, it will work *only* on Win7SP1 - any down stream OS versions will get some bad-juju runtime errors. <br /><br />The issue is discussed in length at <a href="http://social.msdn.microsoft.com/Forums/en/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13">http://social.msdn.microsoft.com/Forums/en/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13</a><br /><br />In short, it sounds like they made some changes to MDAC to make it play more friendly with x64 compilations, but in the process busted it for all older OSes. There's some work-arounds being discussed in the above post, but they have catches, like, if you update your legacy code (not that that's always a possibility,) you loose the ability to compile that code on non-Win7SP1 machines. &nbsp;The only surefire fix right now to not compile your legacy code on Win7SP1 - uninstall SP1 for now until an update is released.<br /><br />Useful tidbits I gleaned:<br /><br /><ul><li><a href="http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13/#97470aa6-1291-4d6a-bad2-90dc7ce52740">http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13/#97470aa6-1291-4d6a-bad2-90dc7ce52740</a>&nbsp;: LONG vs LONGLONG...heh.&nbsp;</li><li><a href="http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13/#1a89662b-f91f-4aef-a706-8fb4d762956d">http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/3a4ce946-effa-4f77-98a6-34f11c6b5a13/#1a89662b-f91f-4aef-a706-8fb4d762956d</a>&nbsp;: Problem fully acknowledge, 'high priority'.</li></ul><div><b>Update </b>2011-Apr-13:&nbsp;</div><div><ul><li><a href="http://connect.microsoft.com/VisualStudio/feedback/details/646313/ado-programs-no-longer-work-on-customer-computers-after-recompiled-on-a-windows-7-sp1-machine">http://connect.microsoft.com/VisualStudio/feedback/details/646313/ado-programs-no-longer-work-on-customer-computers-after-recompiled-on-a-windows-7-sp1-machine</a>&nbsp;Official bug report to MS about the issue - chime in if you're having the same problem!</li></ul></div>Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-59063812072407077732008-12-11T16:21:00.001-05:002008-12-11T16:27:03.193-05:00Delayed Event Handlers and System.Threading.SynchronizationContext<p>I've found myself in situations where I'm on the receiving end of an API that for whatever reason will raise events in (relative) rapid succession.&#160; It just so happens that I need to execute some code when that event is raised, but not with <em>every</em> event raise.&#160; </p> <p>For example, imagine you have a graphical mapping API.&#160; Suppose that when the user pans the map, a &quot;<em>Panned</em>&quot; event is raised.&#160; It turns out to be quite common that a user will pan the map over and over before they finish panning. The processing I need to perform on that <em>Panned</em>&#160; event isn't terribly intensive, but it's not without cost; a cost that is too expensive to incur on every <em>Panned</em> event, to say nothing of the worthlessness of performing this processing before user has finished panning.&#160; </p> <p>A simple answer to that is some form of delayed event processing.&#160; Due to different circumstances at different times I've found myself using two different types of delayed event handlers:</p> <ol> <li>Once the event is raised, it waits for a period of time, and then executes. </li> <li>Once the event is raised, it waits for a period of time to elapse in which no more of the events have been raised. </li> </ol> <p>The first one is analogous to a timed-delay on a camera, or System.Windows.Forms.Timer, or System.Thread.Timer.&#160; The second is more of &quot;I'll wait for you to finish talking before I start talking&quot; kind of thing. Basic implementation entails firing off an asynchronous method that performs the appropriate type of waiting, and once it's finished, the actual processing takes place.&#160; </p> <p>Before getting into the details of this implementation, let's talk about UI-thread synchronization.&#160; If you've ever taken advantage of some thready goodness in order to fork off work, then you may have found out that you are not able to update the UI from the non-UI/main thread.&#160; If you try to touch the UI thread, the Framework will throw an InvalidOperationException telling you: <em>&quot;Cross-thread operation not valid: Control 'TheTextBox' accessed from a thread other than the thread it was created on.&quot;</em> If you've run into this, you've probably googled-up just <a href="http://www.codeproject.com/KB/recipes/AccessControlFromThread.aspx" target="_blank">a</a>&#160;<a href="http://www.shabdar.org/cross-thread-operation-not-valid.html" target="_blank">few</a>&#160;<a href="http://bytes.com/groups/net-vb/389328-cross-thread-operation-not-valid" target="_blank">pages</a>&#160;<a href="http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/2b7621ad-462e-4b6b-a004-0f0110ef6ddb/" target="_blank">about</a>&#160;<a href="http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/507f5d4b-d33e-40e0-8802-05fe46519658/" target="_blank">this</a> exception, and as such, I will not rehash the details here. In short, <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.isynchronizeinvoke.aspx" target="_blank">ISynchronizeInvoke is your answer.</a>&#160; Lucky for almost all cases, <a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.control.aspx" target="_blank">System.Windows.Forms.Control</a> implements ISynchronizeInvoke. It's common enough that in some of my code you'll see a &quot;DoOnUIThread&quot; method that I use in my non-UI-threaded code.&#160; It's pretty simple, take a look:</p> <p></p> <div class="csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #0600ff">private</span> <span style="color: #0600ff">void</span> DoOnUIThread<span style="color: #000000">(</span><span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">ThreadStart</span> method<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #0600ff">if</span> <span style="color: #000000">(</span>this.<span style="color: #0000ff">InvokeRequired</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>&#160; <br />&#160;&#160;&#160;&#160;&#160; this.<span style="color: #0000ff">Invoke</span><span style="color: #000000">(</span>method, <span style="color: #0600ff">null</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160; <span style="color: #000000">}</span> <span style="color: #0600ff">else</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; method<span style="color: #000000">(</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160; <span style="color: #000000">}</span> <br /><span style="color: #000000">}</span></div> <p></p> <p>Where &quot;<em>this</em>&quot; is a Control-derived control, such as a System.Windows.Forms.Form. It's pretty easy to modify this to match whatever delegate you need, and I can only assume there's a simple generic implementation that could accept any delegate type, I just haven't done that yet.&#160; It's easy enough to use:</p> <p></p> <div class="csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0">DoOnUIThread<span style="color: #000000">(</span><span style="color: #ff0000">delegate</span><span style="color: #000000">(</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #008080; font-style: italic">// My UI-bound code goes here, for example:</span> <br />&#160;&#160; _TextBox.<span style="color: #0000ff">Text</span> <span style="color: #008000">=</span> <span style="color: #666666">&quot;Hello There.&quot;</span><span style="color: #008000">;</span> <br /><span style="color: #000000">}</span><span style="color: #000000">)</span><span style="color: #008000">;</span></div> <p>&#160;</p> <p>Unfortunately, there are cases where you're writing something that doesn't have access to a Control-based object.&#160; For example, imagine you're implementing a class library that needs to do threaded operations.&#160; In your unit tests everything seems to run great - your business objects instantiate, thread, calculate and populate.&#160; But unless you also include tests that bind those objects to the UI, you may miss that you can easily get the &quot;<em>Cross-thread operation not valid:</em>&quot; InvalidOperationException only when you bind your business object to the UI. These situations are usually easily remedies by making your objects require an ISynchronizeInvoke object upon instantiation.&#160; That ISynchronieInvoke object can be used to use a <em>DoOnUIThread</em> style method to make sure you don't run into that Cross-thread exception. </p> <p>Even more unfortunate, there are cases where maybe your business objects are so entrenched, or it's simply impractical to require an ISynchronizeInvoke object to be specified upon instantiation. In these cases you're really at a loss for what to do when you need to do some work on a different thread that will eventually need to touch the UI thread.&#160; If you can safely make the assumption that the object will normally be executing in the scope of the UI thread, then you can get your worker-threads back into the context of the UI thread by using System.Threading.SynchronizationContext.&#160; If you make sure to grab a hold System.Threading.SychronizationContext.Current <em>before</em> forking off your worker thread, the worker thread will be able to get back to the UI thread to execute any UI-bound code. </p> <p>Now let's look at an example of this in my implementation of a simple &quot;timer&quot; delayed event handler:</p> <div class="csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #0600ff">private</span> <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">ThreadStart</span> _Click_DelayedEventHandler<span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">SynchronizationContext</span> _SynchronizationContext<span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> <span style="color: #0600ff">void</span> button1_Click<span style="color: #000000">(</span><span style="color: #ff0000">object</span> sender, EventArgs e<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #0600ff">if</span> <span style="color: #000000">(</span>_Click_DelayedEventHandler <span style="color: #008000">!=</span> <span style="color: #0600ff">null</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; return<span style="color: #008000">;</span><span style="color: #008080; font-style: italic">// we're already waiting - bail out</span> <br />&#160;&#160; <span style="color: #000000">}</span> <span style="color: #0600ff">else</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// setup the 'wait until finished' anonymous method:</span> <br />&#160;&#160;&#160;&#160;&#160; _Click_DelayedEventHandler <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">Thread</span>.<span style="color: #0000ff">Sleep</span><span style="color: #000000">(</span><span style="color: #ff0000">1000</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; AsyncCallback finishedWaiting <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span>IAsyncResult ar<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// Take note that at this point you will still be executing on a threadpool thread, </span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// not on the UI thread.</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SendOrPostCallback hello <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span><span style="color: #ff0000">object</span> state<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBox.<span style="color: #0000ff">Show</span><span style="color: #000000">(</span><span style="color: #0600ff">this</span>, <span style="color: #666666">&quot;Hello World&quot;</span>, <span style="color: #666666">&quot;Caption&quot;</span>, MessageBoxButtons.<span style="color: #0000ff">OK</span>, MessageBoxIcon.<span style="color: #0000ff">Exclamation</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _SynchronizationContext.<span style="color: #0000ff">Post</span><span style="color: #000000">(</span>hello, <span style="color: #0600ff">null</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br /> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// Make SURE to cleanup or this will be a one-trick pony</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _Click_DelayedEventHandler <span style="color: #008000">=</span> null<span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _SynchronizationContext <span style="color: #008000">=</span> null<span style="color: #008000">;</span> <span style="color: #008080; font-style: italic">// don't strictly need to set this to null...</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br /> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// fire off our anonymous 'waiter' method on a thread-pool thread:</span> <br />&#160;&#160;&#160;&#160;&#160; _SynchronizationContext <span style="color: #008000">=</span> SynchronizationContext.<span style="color: #0000ff">Current</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; _Click_DelayedEventHandler.<span style="color: #0000ff">BeginInvoke</span><span style="color: #000000">(</span>finishedWaiting, <span style="color: #0600ff">null</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160; <span style="color: #000000">}</span> <br /><span style="color: #000000">}</span></div> <p></p> <p>To try this out, you should be able to create a new Windows Forms project, place a button on the form, hook up this method as the Click event handler. When you run, you should be able to click the button, and no matter how many times you click, you'll only get the &quot;Hello World&quot; message box after one second has elapsed since your first button click. So, what happens is, on the first click, it sets up a ThreadStart delegate, and executes it.&#160; The only thing this does is wait for one second.&#160; If any more button-clicks come in during this second, they're ignored. After the second, the ThreadStart finishes, and the 'finished' callback executes.&#160; It's in this anonymous method that we place our real work.&#160; In the <em>finishedWaiting </em>you can see the use of the UI SynchronizationContext - you create a <em>SendOrPostCallback</em> delegate and .Post it to the UI SynchronizationContext. At the end of <em>finishedWaiting </em>is very important cleanup code.&#160; If you don't set the _Click_DelayedEventHandler back to null, this whole thing is just a one-trick pony.</p> <p>&#160;</p> <p>Here's the more-useful &quot;I'll wait for you to stop clicking&quot; delayed event handler:</p> <div class="csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #0600ff">private</span> <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">ThreadStart</span> _ButtonClick_DelayedEventHandler<span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> DateTime _ButtonClick_LastChangeTime<span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> <span style="color: #ff0000">object</span> _ButtonClick_LastChangeLock <span style="color: #008000">=</span> <a style="color: #000060" href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000">new</span></a> <span style="color: #ff0000">object</span><span style="color: #000000">(</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">SynchronizationContext</span> _ButtonClick_SynchronizationContext<span style="color: #008000">;</span> <br /><span style="color: #0600ff">private</span> <span style="color: #0600ff">void</span> button1_Click<span style="color: #000000">(</span><span style="color: #ff0000">object</span> sender, EventArgs e<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #0600ff">if</span> <span style="color: #000000">(</span>_ButtonClick_DelayedEventHandler <span style="color: #008000">!=</span> <span style="color: #0600ff">null</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// we're already waiting...increment the 'last fired' indicator.</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #0600ff">lock</span> <span style="color: #000000">(</span>_ButtonClick_LastChangeLock<span style="color: #000000">)</span> <span style="color: #000000">{</span> _ButtonClick_LastChangeTime <span style="color: #008000">=</span> DateTime.<span style="color: #0000ff">UtcNow</span><span style="color: #008000">;</span> <span style="color: #000000">}</span> <br />&#160;&#160; <span style="color: #000000">}</span> <span style="color: #0600ff">else</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// setup the 'wait until finished' anonymous method:</span> <br />&#160;&#160;&#160;&#160;&#160; _ButtonClick_DelayedEventHandler <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// this will loop indefinately until the event hasn't been</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// raised for at least 1 second</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TimeSpan difference <span style="color: #008000">=</span> TimeSpan.<span style="color: #0000ff">Zero</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #0600ff">do</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">System.<span style="color: #0000ff">Threading</span></span>.<span style="color: #0000ff">Thread</span>.<span style="color: #0000ff">Sleep</span><span style="color: #000000">(</span><span style="color: #ff0000">100</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br /> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; DateTime lastChanged<span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #0600ff">lock</span> <span style="color: #000000">(</span>_ButtonClick_LastChangeLock<span style="color: #000000">)</span> <span style="color: #000000">{</span> lastChanged <span style="color: #008000">=</span> _ButtonClick_LastChangeTime<span style="color: #008000">;</span> <span style="color: #000000">}</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; difference <span style="color: #008000">=</span> DateTime.<span style="color: #0000ff">UtcNow</span>.<span style="color: #0000ff">Subtract</span><span style="color: #000000">(</span>lastChanged<span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span> <span style="color: #0600ff">while</span> <span style="color: #000000">(</span>difference.<span style="color: #0000ff">TotalMilliseconds</span> <span style="color: #008000">&lt;</span> <span style="color: #ff0000">1000</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; AsyncCallback finishedWaiting <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span>IAsyncResult ar<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SendOrPostCallback hello <span style="color: #008000">=</span> <span style="color: #ff0000">delegate</span><span style="color: #000000">(</span><span style="color: #ff0000">object</span> state<span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBox.<span style="color: #0000ff">Show</span><span style="color: #000000">(</span><span style="color: #0600ff">this</span>, <span style="color: #666666">&quot;Hello World&quot;</span>, <span style="color: #666666">&quot;Caption&quot;</span>, MessageBoxButtons.<span style="color: #0000ff">OK</span>, MessageBoxIcon.<span style="color: #0000ff">Exclamation</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _ButtonClick_SynchronizationContext.<span style="color: #0000ff">Post</span><span style="color: #000000">(</span>hello, <span style="color: #0600ff">null</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br /> <br /> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// Make SURE to cleanup or this will be a one-trick pony.</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _ButtonClick_LastChangeTime <span style="color: #008000">=</span> DateTime.<span style="color: #0000ff">MinValue</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _ButtonClick_DelayedEventHandler <span style="color: #008000">=</span> null<span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _ButtonClick_SynchronizationContext <span style="color: #008000">=</span> null<span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #000000">}</span><span style="color: #008000">;</span> <br /> <br /> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #008080; font-style: italic">// fire off our anonymous 'waiter' method on a thread-pool thread:</span> <br />&#160;&#160;&#160;&#160;&#160; _ButtonClick_SynchronizationContext <span style="color: #008000">=</span> SynchronizationContext.<span style="color: #0000ff">Current</span><span style="color: #008000">;</span> <span style="color: #008080; font-style: italic">// assumed to be the UI thread</span> <br />&#160;&#160;&#160;&#160;&#160; _ButtonClick_LastChangeTime <span style="color: #008000">=</span> DateTime.<span style="color: #0000ff">UtcNow</span><span style="color: #008000">;</span> <br />&#160;&#160;&#160;&#160;&#160; _ButtonClick_DelayedEventHandler.<span style="color: #0000ff">BeginInvoke</span><span style="color: #000000">(</span>finishedWaiting, <span style="color: #0600ff">null</span><span style="color: #000000">)</span><span style="color: #008000">;</span> <br />&#160;&#160; <span style="color: #000000">}</span> <br /><span style="color: #000000">}</span></div> <p> <br />To try this out, do the same steps as the first delayed event handler - hook this up to a button-click.&#160; The difference you'll notice is that as long as you keep clicking the button at least once every second, the message box will not pop up.&#160; It waits for you to finish hammering the button.&#160; Again, it's very important that you cleanup at the end of finishedWaiting, and note that there is more to cleanup this time. </p> <p>Anyway, feel free to customize to your needs, and I really hope these are useful to someone other than myself. </p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com2tag:blogger.com,1999:blog-9236822.post-77670741380087735252008-10-16T11:42:00.001-04:002008-10-16T11:42:40.896-04:00Quick Highlighter - Quick and Easy HTML/CSS Code Highlightin<p>Hey everyone, just a quick post.&#160; I recently learned about <a href="http://quickhighlighter.com/" target="_blank">Quick Highlighter</a>. It will take just about any piece of code you can throw at it and spit out snazzy HTML/CSS syntax highlighter code.&#160; I just used it in my previous post. It's great.</p> <p>I've used the <a href="http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/" target="_blank">CopySourceAsHtml AddIn</a> for Visual Studio 2003/2005/2008, but I've always disliked it because my color scheme in Visual Studio doesn't translate well to HTML.&#160; My blog has a dark-text-on-light-background scheme while I code in a light-text-on-dark-background scheme.&#160; Plus, it doesn't help me when I want to post a piece of VB6 code, or code in any other language for that matter.</p> <p>So, give it a try.&#160; I like it. :)</p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-5115800132672261522008-10-16T11:37:00.001-04:002008-10-16T11:37:19.966-04:00Troubles Marshalling System.Guid to Visual Basic 6.0 (VB6)<p>I find myself in the &lt;finger-quote&gt;interesting&lt;/finger-quote&gt; situation of having to interop a large portion of .NET code back into an older VB6 code base. As it turns out, I need to pass a System.Guid to VB6. Before exposing a property that returned a System.Guid I made sure that System.Guid was COM-Visible - it is:</p> <div class="csharp csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #000000">[</span>Serializable<span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>ComVisible<span style="color: #000000">(</span><span style="color: #0600ff">true</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #0600ff">public</span> <span style="color: #ff0000">struct</span> Guid <span style="color: #008000">:</span> IFormattable, IComparable, IComparable<span style="color: #008000">&lt;</span>Guid<span style="color: #008000">&gt;</span>, IEquatable<span style="color: #008000">&lt;</span>Guid<span style="color: #008000">&gt;</span> <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #008080; font-style: italic">// remainder of metadata snipped for brevity</span> <br /><span style="color: #000000">}</span></div> <p>But let's get this point across right from the beginning: System.Guid is marked as being COM-Visible, <i>but it is not usable in VB6</i>! After adding a reference to mscorlib, VB6 will allow you to create a variable of type System.Guid:</p> <div class="vb vb" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #b1b100">Dim</span> myGuid <span style="color: #b1b100">as</span> mscorlib.<span style="color: #66cc66">Guid</span>&#160; </div> <p>But you are unable to actually make use of 'myGuid' variable.&#160; There is no intellisense pop-up when you type 'myGuid.', there's no way to create one or populate a declared mscorlib.Guid </p> <p>I'm not entirely sure what the problem is, but I have to assume it has to do with Guid being a struct. Regardless of the reason for the problem, I still needed access to a System.Guid in VB6. Thankfully, all my problem requires is to fetch the Guid from a .NET object, and then pass that Guid to a stored procedure that's expecting a SQL Server &quot;uniqueidentifier&quot; data type. </p> <p>I started searching. Talk about a tough problem to google for!&#160; There's a lot of really good hits regarding Guid's an creating COM-visible classes/interfaces, but I found very little info on actually marshalling a System.Guid back-n-forth to/from .NET. Thankfully, I was able to <a href="http://social.msdn.microsoft.com/Forums/en-US/vbinterop/thread/16c0d49e-48d2-4329-8904-a2c371c5fba7/" target="_blank">find this post</a> where someone is experiencing the exact same problem.&#160; Unfortunately, there was no good answer. Someone did post a reply which didn't answer the original poster's question, but does include a link to an <a href="http://blogs.msdn.com/adam_nathan/archive/2003/04/23/56635.aspx" target="_blank">Adam Nathan blog post discussing customizing marshalling</a>, which uses a System.Guid as an example. It's informative, but it doesn't offer any assistance with the problem.</p> <p>For verbosity (and the morbidly curious,) let's look at a simple example illustrating the problem.&#160; Here's an interface and a class that implements that interface, all exposed to COM. (This is in C#, but can just as easily be written in VB.NET.)</p> <div class="csharp csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #000000">[</span>Guid<span style="color: #000000">(</span><span style="color: #666666">&quot;BD95337B-0395-4773-B2FC-2BC812388BAB&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>InterfaceType<span style="color: #000000">(</span>ComInterfaceType.<span style="color: #0000ff">InterfaceIsIDispatch</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>ComVisible<span style="color: #000000">(</span><span style="color: #0600ff">true</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #0600ff">public</span> <span style="color: #ff0000">interface</span> IGuidGetter <span style="color: #000000">{</span> <br />&#160;&#160; Guid GetSessionID<span style="color: #000000">(</span><span style="color: #000000">)</span>; <br /><span style="color: #000000">}</span> <br />&#160; <br /><span style="color: #000000">[</span>Guid<span style="color: #000000">(</span><span style="color: #666666">&quot;6AAFF9A3-875D-4955-AE09-634A88D9EC56&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>ClassInterface<span style="color: #000000">(</span>ClassInterfaceType.<span style="color: #0000ff">None</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>ComVisible<span style="color: #000000">(</span><span style="color: #0600ff">true</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #000000">[</span>ProgId<span style="color: #000000">(</span><span style="color: #666666">&quot;JTPApp.GuidGetter&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span> <br /><span style="color: #0600ff">public</span> <span style="color: #ff0000">class</span> GuidGetter <span style="color: #008000">:</span> IGuidGetter <span style="color: #000000">{</span> <br />&#160;&#160; <span style="color: #0600ff">public</span> Guid GetSessionID<span style="color: #000000">(</span><span style="color: #000000">)</span> <span style="color: #000000">{</span> <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #0600ff">return</span> Guid.<span style="color: #0000ff">NewGuid</span><span style="color: #000000">(</span><span style="color: #000000">)</span>; <br />&#160;&#160; <span style="color: #000000">}</span> <br /><span style="color: #000000">}</span> </div> <p>To use that class in VB6, we compile it, regasm it, and add the registered type lib as a reference to the VB6 project. We can now do something like:</p> <div class="vb vb" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #b1b100">Dim</span> myGuidGetter <span style="color: #b1b100">As</span> <span style="color: #b1b100">New</span> GuidGetter <br /><span style="color: #b1b100">Dim</span> myGuid <span style="color: #b1b100">As</span> mscorlib.<span style="color: #66cc66">Guid</span> <br /> <br />myGuid = guidGetter.<span style="color: #66cc66">GetSessionID</span><span style="color: #66cc66">(</span><span style="color: #66cc66">)</span> <br /></div> <p>Unfortunately, Line 4 will fail to compile, giving you a <strong><em>&quot;Variable uses an automation type not supported in Visual Basic&quot;</em></strong> error message.</p> <p> <br />Decorating the .NET interface and/or implementation with [MarshalAs(UnmanagedType.LPStruct)], while it does change the COM IDL (as noted and demonstrated in <a href="http://blogs.msdn.com/adam_nathan/archive/2003/04/23/56635.aspx" target="_blank">Adam Nathan's article</a>,) doesn't make any difference from a VB6 standpoint. You always get the <em>&quot;Variable uses an automation type not supported in Visual Basic&quot;</em> error. </p> <p> <br />The really aggravating part for me is that I don't need to really use the Guid in VB6.&#160; I simply need VB6 to retrieve it, and then stuff it into a stored procedure call!&#160; </p> <p>With this in mind, I tried returning the Guid as an object, but figured it would have problems because I would be boxing a value type in a reference type and then expecting VB6 to know how to properly unbox it. VB6 was able to retrieve the Guid into a Variant, but upon trying to set it as a parameter to the stored procedure, I received an AccessViolationException in my .NET code. Not pretty.</p> <p>Thankfully, my work-around to the problem turns out to be pretty simple: Return the Guid as a string.&#160; So, to follow through with the example code I started above, if you change it to look like:</p> <p></p> <div class="csharp csharp" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"><span style="color: #000000">[</span>Guid<span style="color: #000000">(</span><span style="color: #666666">&quot;BD95337B-0395-4773-B2FC-2BC812388BAB&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #000000">[</span>InterfaceType<span style="color: #000000">(</span>ComInterfaceType.<span style="color: #0000ff">InterfaceIsIDispatch</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #000000">[</span>ComVisible<span style="color: #000000">(</span><span style="color: #0600ff">true</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #0600ff">public</span> <span style="color: #ff0000">interface</span> IGuidGetter <span style="color: #000000">{</span>&#160;&#160; <br />&#160;&#160; <span style="color: #ff0000">string</span> GetSessionID<span style="color: #000000">(</span><span style="color: #000000">)</span>;&#160;&#160; <br /><span style="color: #000000">}</span>&#160;&#160; <br />&#160; <br /><span style="color: #000000">[</span>Guid<span style="color: #000000">(</span><span style="color: #666666">&quot;6AAFF9A3-875D-4955-AE09-634A88D9EC56&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #000000">[</span>ClassInterface<span style="color: #000000">(</span>ClassInterfaceType.<span style="color: #0000ff">None</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #000000">[</span>ComVisible<span style="color: #000000">(</span><span style="color: #0600ff">true</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #000000">[</span>ProgId<span style="color: #000000">(</span><span style="color: #666666">&quot;JTPApp.GuidGetter&quot;</span><span style="color: #000000">)</span><span style="color: #000000">]</span>&#160;&#160; <br /><span style="color: #0600ff">public</span> <span style="color: #ff0000">class</span> GuidGetter <span style="color: #008000">:</span> IGuidGetter <span style="color: #000000">{</span>&#160;&#160; <br />&#160;&#160; <span style="color: #0600ff">public</span> <span style="color: #ff0000">string</span> GetSessionID<span style="color: #000000">(</span><span style="color: #000000">)</span> <span style="color: #000000">{</span>&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160; <span style="color: #0600ff">return</span> Guid.<span style="color: #0000ff">NewGuid</span>.<span style="color: #0000ff">ToString</span><span style="color: #000000">(</span><span style="color: #000000">)</span>;&#160;&#160; <br />&#160;&#160; <span style="color: #000000">}</span>&#160;&#160; <br /><span style="color: #000000">}</span>&#160; </div> <p></p> <p>It will work.&#160; You get the Guid, as a string, in VB6.&#160; There's only one minor catch to getting SQL Server to be able to convert that string into a unique identifier, it must be enclosed in curly braces, like so:</p> <p></p> <div class="vb vb" style="border-right: #d0d0d0 1px solid; border-top: #d0d0d0 1px solid; border-left: #d0d0d0 1px solid; color: #006; border-bottom: #d0d0d0 1px solid; font-family: monospace; background-color: #f0f0f0"> <ol> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace"><span style="color: #b1b100">Dim</span> sqlCommand <span style="color: #b1b100">As</span> <span style="color: #b1b100">New</span> ADODB.<span style="color: #b1b100">Command</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace"><span style="color: #b1b100">With</span> sqlCommand </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">ActiveConnection</span> = dbConn </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">CommandText</span> = <span style="color: #ff0000">&quot;A_Stored_Procedure&quot;</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">CommandType</span> = adCmdStoredProc </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">Parameters</span>.<span style="color: #66cc66">Refresh</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; <span style="color: #b1b100">Dim</span> tmp <span style="color: #b1b100">As</span> <span style="color: #b1b100">String</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; tmp = g_UserInfo.<span style="color: #66cc66">GetSessionID</span><span style="color: #66cc66">(</span><span style="color: #66cc66">)</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">Parameters</span><span style="color: #66cc66">(</span><span style="color: #ff0000">&quot;@sessionID&quot;</span><span style="color: #66cc66">)</span>.<span style="color: #66cc66">Value</span> = <span style="color: #ff0000">&quot;{&quot;</span> &amp; tmp &amp; <span style="color: #ff0000">&quot;}&quot;</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace">&#160;&#160; .<span style="color: #66cc66">Execute</span> , , adExecuteNoRecords </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace"><span style="color: #b1b100">End</span> <span style="color: #b1b100">With</span> </div> </li> <li style="font: 130% &#39;Courier New&#39;, courier, monospace; color: #003030"> <div style="padding-right: 0px; padding-left: 0px; font-weight: normal; padding-bottom: 0px; margin: 0px; color: #000020; padding-top: 0px; font-style: normal; font-family: monospace"><span style="color: #b1b100">Set</span> sqlCommand = <span style="color: #b1b100">Nothing</span> </div> </li> </ol> </div> <p>On line 10, you can see that I wrapped it in curly-braces.&#160; So while this works for my scenario, there's no way, that I've figured out, to easily interop a System.Guid value back and forth between .NET and VB6.</p> <p>Anybody have useful insight in how to get the back-n-forth marshalling to work?&#160; I can't imagine I'm the only one who's ever needed to do this.</p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-1127446141479361852008-07-20T14:25:00.003-04:002008-07-20T15:39:52.102-04:00Panographic Play using AutostitchI've been playing with <a href="http://www.cs.ubc.ca/~mbrown/autostitch/autostitch.html">Autostitch</a> for a while now. It's a proprietary, but free, tool for automatically stitching and blending photographs together into panographic mosaics. <br /><br />For any of these pictures, click them and go download the original to see the full-size stitch.<br /><br />Here's a link to the entire album:<br /><table style="width:194px;"><tr><td align="center" style="height:194px;background:url(http://picasaweb.google.com/f/img/transparent_album_background.gif) no-repeat left"><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos?authkey=NPUHSQ_icBU"><img src="http://lh5.ggpht.com/jtpoll/SINn9l7stoE/AAAAAAAAB1U/6RaFFw6qd2g/s160-c/StitchedPanographicPhotos.jpg" width="160" height="160" style="margin:1px 0 0 4px;"></a></td></tr><tr><td style="text-align:center;font-family:arial,sans-serif;font-size:11px"><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos?authkey=NPUHSQ_icBU" style="color:#4D4D4D;font-weight:bold;text-decoration:none;">Stitched Panographi<wbr></wbr>c Photos</a></td></tr></table><br /><br />Here's a few from the album:<br /><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos/photo?authkey=NPUHSQ_icBU#5225137844131289506"><img src="http://lh4.ggpht.com/jtpoll/SINrL23lWaI/AAAAAAAAByY/UPgANTGbW-k/s400/pano.jpg" /></a><br />This is the bridge over the Eagle River in Eagle River. Any family members recognize this stitch from the family blog? This is the raw stitch before cropping and editing. <br /><br /><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos/photo?authkey=NPUHSQ_icBU#5225173054380909410"><img src="http://lh6.ggpht.com/jtpoll/SIOLNXRRI2I/AAAAAAAABzI/FQiRFn100uw/s400/deltsigs_carny_pano.jpg" /></a><br />Here's a shot from Winter Carnival 2008. This is the delt-sigs statue. Again, download the original full-size image and check out the detail. :) (This could use some contrast-adjusting...)<br /><br /><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos/photo?authkey=NPUHSQ_icBU#5225134920178767074"><img src="http://lh4.ggpht.com/jtpoll/SINohqS-lOI/AAAAAAAABxQ/yNe9lHQ57H4/s400/pano_large.jpg" /></a><br />This is the lower Hungaria falls. This was a simple three-photo stitch. I had to rotate the pictures 90 degrees before stitching.<br /><br /><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos/photo?authkey=NPUHSQ_icBU#5225136694210536098"><img src="http://lh6.ggpht.com/jtpoll/SINqI7FHIqI/AAAAAAAABx0/PM0c82s6ulY/s144/pano.jpg" /></a><br />This is the spring 2008 run-off on the Tioga River in between Houghton and Marquette at a roadside park. It's just outside of Alberta by a few miles. I had photos to go in the bottom left of the photo, but autostitch didn't include them in the stitch for whatever reason.<br /><br /><a href="http://picasaweb.google.com/jtpoll/StitchedPanographicPhotos/photo?authkey=NPUHSQ_icBU#5225136944746371330"><img src="http://lh6.ggpht.com/jtpoll/SINqXgZgmQI/AAAAAAAABx8/0pQttV4Mfr4/s400/pano.jpg" /></a><br />This is another of the Tioga river. <br /><br />There's more in the album, so click-through to see them all.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-77059480702449372812008-05-20T22:14:00.004-04:002008-05-20T22:42:52.293-04:00XPS Documents Opening With Firefox in VistaSo there I was, I had completed a transaction at a <span style="font-style: italic;">'save this page for your records'</span> page that I wanted to a PDF. Ooops...I don't have Acrobat on this laptop. Luckily I knew I had the XPS 'printer' installed by default. (It comes with Vista.)<br /><br />So I print it to an .xps file. it appears to 'print' successfully. The problem is, when I doublt-click it to open it, it opens in Firefox! Firefox is set as my default browser, but I find this weird - I thought there was an XPS viewer application. I right-click the .xps file and choose <span style="font-style: italic;">"Open With"</span> and then "XPS Viewer". Again, it tries to open in Firefox. Firefox doesn't understand the .xps extension and offers to save the file for me, or let's me open it with "XPSViewer.Document (default)". Huh. Ok. I tell Firefox to open it with the "XPSViewer.Document (default)". Firefox opens yet another tab with the .xps document, offering again to save/open it for me.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qt9sjR3SvlA/SDOJ1uCOQNI/AAAAAAAABdg/GQ1GR4a3uTE/s1600-h/vista_firefox_xps.png"><img style="cursor: pointer;" src="http://2.bp.blogspot.com/_qt9sjR3SvlA/SDOJ1uCOQNI/AAAAAAAABdg/GQ1GR4a3uTE/s400/vista_firefox_xps.png" alt="" id="BLOGGER_PHOTO_ID_5202653550526480594" border="0" /></a><br /><br /><br /><span style="font-style: italic;">Okaaaaay. </span><br /><br />I caught wind that you should try dropping the file into Internet Explorer. This works. IE recognizes the file and opens the XPS document for you. I also caught wind that this isn't just a Firefox problem, but rather a "default browser is not IE" problem. One solution to the problem is to re-associate the .xps file extension with IE instead of the "XPS Viewer" application. The other route (which I prefer) is to download the <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=b8dcffdd-e3a5-44cc-8021-7649fd37ffee&amp;DisplayLang=en">"XPS Essentials" pack from Microsoft</a>. I think this pack was originally intended to add XPS functionality to XP and Server 2003, but it now supports Vista, and it appears to merge into Vista as some sort of update.<br /><br />After I installed this update, double-clicking an .xps file still opened it in Firefox though! Come on! If I right-click on the .xps file, and choose <span style="font-style: italic;">"Open With"</span>, I now have a new item: <span style="font-style: italic;">"XPS Viewer EP"</span>. Ah-ha! This opens up the .xps file in it's own separate non-browser viewer. Perfect!<br /><br />All that's left to do is make <span style="font-style: italic;">XPS Viewer EP</span> the default app for .xps files. The first time I tried changing a file extension association in Vista, I went about it the way you do in XP - go to the <span style="font-style: italic;">Tools->Folder Options</span> menu. But in Vista, the dialog that get for <span style="font-style: italic;">Folder Options</span> doesn't have the familiar file associations tab that XP has. In Vista, you have to use the <span style="font-style: italic;">"Default Programs"</span> Start menu item. (Just use the quick-search, or you should be able to find it under the Control Panel:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qt9sjR3SvlA/SDOJ1OCOQMI/AAAAAAAABdY/bVjb_IoPmlQ/s1600-h/vista_default_programs.png"><img style="cursor: pointer; width: 501px; height: 236px;" src="http://4.bp.blogspot.com/_qt9sjR3SvlA/SDOJ1OCOQMI/AAAAAAAABdY/bVjb_IoPmlQ/s400/vista_default_programs.png" alt="" id="BLOGGER_PHOTO_ID_5202653541936545986" border="0" /></a><br /><br />Just hit the <span style="font-style: italic;">''Associate a file type or protocol with a program"</span> item and you should be able to figure it out from there.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com13tag:blogger.com,1999:blog-9236822.post-35077171949165862602008-04-11T15:08:00.002-04:002008-04-12T18:15:16.950-04:00AnkhSVN and VS2008I found <a href="http://damieng.com/blog/2007/06/13/ankhsvn-and-visual-studio-20072008orcas">this post by Damien Guard</a> useful on getting AnkhSVN to work with VS2008.<br /><br />Sorry for such a short post...Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-60319732638699635332008-04-02T11:31:00.001-04:002008-04-02T11:31:49.768-04:00Rolling Back An SVN Repository<p>Yeesh...&#160; Talk about a minor panic.</p> <p>Don't ask me how, because I'm not entirely sure what I did to make it happen.&#160; But, I <em>thought</em> I went to commit some code, and when I hit 'commit', instead of committing the code, it decided that I had flagged my entire working directory and the point in the repository for deletion.&#160; </p> <p>I really don't know what happened.</p> <p>I thought I'd fix the problem by simply pulling out my stuff at a previous revision, and recommitting. This seemed problematic because I'd have to re-add the trunk, and there would still be a delete in the history, and I'm sure that could cause merge-hell in the future.&#160; </p> <p>Some quick googling turned up <a href="http://timhatch.com/ark/2006/04/18/howto-svn-commit-rollback" target="_blank">Tim Hatch's very simple instructions on how to rollback an SVN commit</a>. Tim apparently accidentally checked some confidential information into his public repository and explains how to rollback a revision by doing something really dirty: manually modifying your repository.</p> <p>I won't rehash what he explains, but know that his solution worked for our FSFS based repository. </p> <p>&#160;</p> <p>Thanks Tim!</p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com1tag:blogger.com,1999:blog-9236822.post-71686821890822915722008-03-11T20:10:00.002-04:002008-03-11T20:18:23.278-04:00TumblrFor the three of you who read my blog, and the two of you who subscribe to this blog's RSS feed, I'm here to say you may want to also sub to my <a href="http://yoopergeek.tumblr.com/rss">tumblr feed</a>. <br /><br />Actually, you may want to sub to that and unsub from this because the tumblr feed aggregates this blog feed. It's up to you I guess.<br /><br />Why tumblr? Because I'm not cool enough for twitter, and honestly, I like the extra content-type features tumblr offers. My primary reason for creating a tumblr account is because I have my <a href="http://www.google.com/reader/shared/14766863855758347747">Google Reader Shared Items</a> feed, (shown at right, and also aggregated by my tumblr feed,) but I've ran into a lot of things that aren't on a feed and thusly can't be easily 'shared'.<br /><br />So, that'<a href="http://yoopergeek.tumblr.com">s yoopergeek.tumblr.com</a>, and it's <a aiotitle="RSS feed" href="http://yoopergeek.tumblr.com/rss">RSS feed</a>.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com2tag:blogger.com,1999:blog-9236822.post-60341414762163097322008-03-11T20:08:00.001-04:002008-03-11T20:10:17.921-04:00A little extra EVDO infoIn my last post about my new-found broadband, I should have linked <a href="http://evdoinfo.com">EVDOInfo.com</a> Specifically, their <a href="http://www.evdoinfo.com/content/view/37/61/">"What is EVDO?"</a> page.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-18287650612805376962008-03-09T22:59:00.004-04:002008-03-09T23:28:55.580-04:00Current ReadingOver a year ago, I subscribed to <a href="http://www.makezine.com/magazine/">MAKE Magazine</a> after following the <a href="http://www.makezine.com/">MAKE RSS feed</a> for probably 6 months before that. I loved the magazine, but felt slightly left out of many of the projects being covered.<br /><br />See, while I have a lot of software development background, I have very little electronics background. I did grow up playing with those <a href="http://www.amazon.com/Vintage-Sports-Cards-MX-906-Electronic/dp/B00005K2SY">"100-in-1 Electronics Projects" kits </a>but electronics never really took off in my mind. <br /><br />I've taken a couple (pathetic) stabs at it in the past, but I always lost interest when I couldn't find any good introductory reading material. The things I would read seemed so far detached form the level that I wanted to be involved with. (Does that makes sense?) I knew I had to learn about the low-level concepts such as electromotive force, current, power, and how they relate to each other, but everything I tried reading just didn't seem to hit the right 'buttons' in my head to click. <br /><br />After watching the <a href="http://revision3.com/systm/avr101/">AVR Episode</a> of <a href="http://revision3.com/systm/">Systm</a>, I thought "holy crap, I can do that, and these AVR microprocessors? They seem right up my alley!" I don't know what it was, but there was something about that episode that made electronics, at least simple microprocessor-based electronics, feel accessible to me: <span style="font-style: italic;">Wire up a simple chip-programmer. Write some code. Compile some code. Push code to chip. Blinky LED!!</span><br /><br />I really think it had to do with the simplicity of the programmer, and the <a href="http://www.instructables.com/id/Ghetto-Programming%3a-Getting-started-with-AVR-micro/">hold-your-hands instructions they linked to</a> that grabbed me and said: "You're gonna learn another hobby, buck-o."<br /><br />I played a bit on a breadboard, never actually making that AVR programmer (yet). I again thought: "Yeah...this is cool...but I still need a good book." I dug around for a while, and of course, there's a ton of books on <a href="http://www.google.com/search?q=Learning+electronics">"learning electronics"</a>...kinda makes picking one a tough choice. <br /><br />Luckily, while on Amazon I was reading a scathing review for some book that I don't remember. The reviewer mentioned "Gibilisco's book", and how great it was for the total newbie. "Not too in depth, not too light." The freakin' just-right porridge of the 'learning-electronics' book world is what it sounded like!<br /><br />I searched, found, and purchased Gibilisco's book and have been reading it (slowly) for over a week now, and I have to agree with that reviewers sentiment: So far, this book is good for beginners. I like that it's easy to read and understand, and at the end of every chapter, there's a test. It feels like a book from school in some ways...but without that $250 price tag. <br /><a href="http://www.amazon.com/gp/product/0071459332?ie=UTF8&amp;tag=yoopergeek-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0071459332"><img src="http://1.bp.blogspot.com/_qt9sjR3SvlA/R9SpNamVNjI/AAAAAAAABQ0/0AF8tQ5mweo/s400/31TGVcgJ5qL._AA_SL160_.jpg" border="0" /></a><img src="http://www.assoc-amazon.com/e/ir?t=yoopergeek-20&amp;l=as2&amp;o=1&amp;a=0071459332" alt="" style="border: medium none ! important; margin: 0px ! important; display: none;" border="0" height="1" width="1" /><br /><br />I'll probably write more about this as the time goes on. But from what I've read so far of this book, I like it, and I'd recommend it to any 'geeky' individual who just hasn't found the right reading material to get them into electronics.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com2tag:blogger.com,1999:blog-9236822.post-4202256500180308842008-03-09T21:22:00.003-04:002008-03-09T22:19:33.764-04:00Motorola ROKR (MOTOROKR Z6m) + Alltel EVDO == "Finally, broadband in my home!"My family and I live in one of the <a href="http://maps.live.com/?v=2&sp=Point.rvkw487mn1t9_Work_I%20work%20here._http%3A%2F%2Fwww.roadsoft.org%2F_&encType=1">few remaining middle-of-nowheres</a>. There's plenty of nice things about our <a href="http://www.cityofhoughton.com/">middle-o-nowhere</a>: it's cozy, it has just about everything you need and it's surrounded by pretty much nothing but <a href="http://pasty.com/cam/index.html">beautiful sites</a> for at least 100 miles. Heck, it even has bandwidth options out the wa-zoo: <a href="http://www.charter.com">cable</a>, <a href="http://www.att.com">DSL</a>, and even <a href="http://www.pasty.net">line-of-sight wireless DSL</a>. Naturally, we bought a house that's just far enough out of town to not have access to any of these options.<br /><br />"But I work in front of a computer all day...surely I don't need/want broadband at home?" Yeah...I kept telling myself that for the last 8 years. The <a href="http://www.johndee.com/history.htm">winters are just too long</a> around here to not have broadband. Of course, I've always had the satellite option, but it just wasn't for me -- large initial setup costs, and then hefty monthly fees, mixed with latency issues. <br /><br />Thankfully, finally, as I sit here and type this, I'm on something other than dialup, and man, I gotta say, Alltel has got a decent thing going right now. I wish I could say I'm being paid for this free advertising, but I'm not. I'm just thrilled to the core to have bandwidth!<br /><br />I recently got a new cell phone, the <a href="http://direct.motorola.com/hellomoto/motorokrz6m/">Motorola ROKR Z6m</a>. I won't go into the snazzy details of the phone -- click that link if you want details.<br /><br />We've had an Alltel contract for nearly two years now. A while back, as I drove home from work, I noticed the Alltel store had a banner outside proclaiming "$25 HIGH SPEED INTERNET FOR YOUR PC!" Huh. I eventually got around to checking it out. It's just that. $25/month, for, as far as I can tell, truly unlimited data transfer. I've heard it called 'tethered' internet because you have to hook your phone to you computer via USB (or possibly bluetooth...) Anyway, it's nice in that it's not a contracted service - you can turn add/remove it from your plan whenever you want. <br /><br />Now, if you're reading this and you don't have broadband, and you're considering going with this Alltel-route, maybe I can save you some money and trouble. Visit the <a href="http://www.alltel.com/wps/portal/AlltelPublic/Content?WCM_GLOBAL_CONTEXT=/wps/wcm/connect/Personal/home/p/internetaccess/dinternetaccess">alltel page for some brief details</a>. Scroll all the way to the bottom and you'll see the "Wireless Internet Kit" which strangely, includes a cable. All this kit has are cable(s) for hooking your phone to your PC's USB port, and some software/drivers on a CD.<br /><br />If you have a phone that has a USB connection on it, just pick up the most affordable USB cable you can find. If not, well, you may want to spring for the $60 kit...or maybe splurge and upgrade to a new phone? Make sure it's an EVDO phone! For the software, all you need are the USB drivers. You can download these drivers <a href="https://developer.motorola.com/docstools/USB_Drivers/">directly from Motorola</a> (free registration required to download.) After downloading and installing the drivers, but before hooking your phone to your PC, make sure your phone is set to work as a modem when hooked up via USB, this should be buried under the "Connection" settings. <span style="font-style:italic;">Now </span>hook your phone to your PC.<br /><br />Assuming you have your internet plan turned on, your phone has signal, your drivers are 'happy' with the phone, all you need to do to get connected is to setup a new dialup internet connection in Windows (sorry, I'm not a Mac guy, and I've not the desire to make this work under *nix.) The number you need to dial will be "#777". For username and password, use "[YourPhoneNumber]@alltel.net", ie: 1235551212@alltel.net. The password should be "alltel".<br /><br />Dialup and you should be connected!<br /><br />Check me out! It's not amazing, but you must admit it's a <span style="font-style:italic;">ton </span>better than dialup. :)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qt9sjR3SvlA/R9SWOamVNiI/AAAAAAAABQs/2IaEEPi0qN4/s1600-h/speedtest.png"><img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qt9sjR3SvlA/R9SWOamVNiI/AAAAAAAABQs/2IaEEPi0qN4/s400/speedtest.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5175927046157973026" /></a><br /><br />PS: I've left out some details here as it's getting late, but big thanks to Mikie and mom for helping get my contract squared away, and thank you interpipes for offering up the answers as you always do. ;)Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com6tag:blogger.com,1999:blog-9236822.post-73279109170991029152007-11-14T18:35:00.001-05:002007-12-06T10:31:10.650-05:00Cloaking/Hiding/Filtering Unwanted Directories During Checkout (aka "Partial Checkouts") With Subversion 1.4.x<h2>The Problem:</h2> <p>Ever find yourself working with a large-ish SVN repository that includes many sub-projects, and you only want to work on a subset of those sub-projects, but you don't want to deal with all the extra cruft involved in checking out at the top level of the repository?</p> <p><em>&quot;What's this guy talking about?&quot;</em></p> <p>Sorry, a picture will help me explain.&#160; Below is a snapshot of an old repository of <a href="http://merl.michiganltap.org" target="_blank">one of products</a> we develop here at work:</p> <p><a href="http://lh5.google.com/jtpoll/RzuGJsVXx2I/AAAAAAAABE8/lXGjGHcOJVY/00_source_repo_browse%5B8%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="388" alt="00_source_repo_browse" src="http://lh4.google.com/jtpoll/RzuGKcVXx3I/AAAAAAAABFE/6DQ7-2iRvO4/00_source_repo_browse_thumb%5B6%5D.png" width="479" border="0" /></a></p> <p>As you can see, trunk consists of multiple sub-components, each of which have their own branches/tags/trunk folders.&#160; Whether or not this is the best repository layout could be up for debate, but it seems to treat us well with the design of this project. (We have a repository layout similar to this for <a href="http://www.roadsoft.org" target="_blank">another project</a> we have, and it seems to serve us well there as well.)</p> <p>The big problem we have with this repository layout is that you never want to checkout that top-level trunk.&#160; You end up with all of the sub-branches and tags folders that are not only superfluous, but also waste space and make the commit or update process so much slower because SVN must check all the sub-folders for modifications.</p> <p>What ends up happening, for example, is someone needs to work on the &quot;Bids&quot; and &quot;Main&quot; components at the same time.&#160; To facilitate this without checking out at the trunk-level is they create two separate working folders, and check out each respective component's trunk (or appropriate branch).&#160; When they commit, they have to make two separate commits.</p> <p>While this works, it's not desirable, especially if you aspire to reach <a href="http://en.wikipedia.org/wiki/Continuous_Integration">Continuous Integration</a> nirvana. Not being able to create an atomic commit across various working folders could potentially break things in a CI environment if changes in one component rely upon the changes of another.&#160; Even if CI isn't your eventual goal, an atomic commit of 'grouped changes' just makes sense.</p> <p><em>&quot;What to do?&quot;</em>&#160; </p> <h2>The Fix:</h2> <p>Well, the good news is that Subversion 1.5 is supposed to have better support for 'partial checkouts'.&#160; I'll admit I haven't kept up with that aspect of v1.5, to be totally honest, I'm more excited about <a href="http://blogs.open.collab.net/svn/2007/05/the_subversion__1.html">merge tracking</a>. But that's for another post.</p> <p>But anyway, there is a work-around in SVN 1.4.x (and probably earlier versions..sorry for not doing any fact-checking here.)&#160; Well, maybe work-around isn't the right word, it's more of an off-label use of a repository feature.</p> <p>Before continuing on, you must know that I'm primarily a Windows developer.&#160; I use <a href="http://subversion.tigris.org">Subversion</a> on Windows via the excellent <a href="http://tortoisesvn.tigris.org/">TortoiseSVN</a> front end. The following how-to is going to be using TortoiseSVN.&#160; Everything that follows is entirely possible with the command line SVN tools, and, if there's enough request, I'll update this post with how to do it from the command line. (<a href="mailto:jtpoll@gmail.com">email me!</a>)</p> <p><em>&quot;Get to it already, would ya?&quot;</em></p> <p>Sorry, here we go, in short:</p> <ol> <li>Create a repository on your local machine. </li> <li>Checkout a working copy of the repository and specify an svn:external property that will link to the portions of the remote repository you really want to work with. (Read the <a href="http://svnbook.red-bean.com/en/1.4/svn.advanced.externals.html">SVN Book</a> for important notes and details.) </li> <li>Commit this svn:externals property back to the local repository. </li> <li>Update you working copy of the local repository and you'll have checked out the parts of the remote repository you want to work with. </li> </ol> <h2>Details:</h2> <p>Create a new folder somewhere on your machine, make sure it's in a place that you won't forget, and in non-obtrusive location because once this is created, it can't be moved without causing you pain in the process. ;)</p> <p><a href="http://lh6.google.com/jtpoll/RzuGK8VXx4I/AAAAAAAABFM/HI32vE_eNvg/01_myfilterrepo%5B2%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="105" alt="01_myfilterrepo" src="http://lh3.google.com/jtpoll/RzuGLMVXx5I/AAAAAAAABFU/FTbBBdGhcpQ/01_myfilterrepo_thumb.png" width="115" border="0" /></a></p> <p><em>MyFilterRepo</em> is a folder on my desktop.&#160; Now, using TortoiseSVN, create an empty repository inside that new folder.&#160; Right click on the folder, choose&#160; <em>TortoiseSVN -&gt; Create repository here...</em>&#160; Choose the 'FSFS' file system type.</p> <p><a href="http://lh4.google.com/jtpoll/RzuGLcVXx6I/AAAAAAAABFc/lK8Ah0hRFFs/02_create_repo%5B6%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="204" alt="02_create_repo" src="http://lh6.google.com/jtpoll/RzuGL8VXx7I/AAAAAAAABFk/L_iZrPPQAe8/02_create_repo_thumb%5B4%5D.png" width="407" border="0" /></a>&#160;</p> <p>Then, browse this repository, right-click the folder, <em>TortoiseSVN-&gt;Repo-browser:</em></p> <p><a href="http://lh3.google.com/jtpoll/RzuGMMVXx8I/AAAAAAAABFs/hFLcpbaD2-8/03_repo_browse%5B5%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="97" alt="03_repo_browse" src="http://lh4.google.com/jtpoll/RzuGMcVXx9I/AAAAAAAABF0/YT0lH6VSueg/03_repo_browse_thumb%5B3%5D.png" width="415" border="0" /></a></p> <p>Create a 'trunk' folder (or any other folder...it doesn't really matter:)</p> <p><a href="http://lh6.google.com/jtpoll/RzuGM8VXx-I/AAAAAAAABF8/jrLxRRT2vek/04_create_trunk%5B5%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="240" alt="04_create_trunk" src="http://lh4.google.com/jtpoll/RzuGNcVXx_I/AAAAAAAABGE/45w0DxKs2bc/04_create_trunk_thumb%5B3%5D.png" width="421" border="0" /></a></p> <p>Create another empty folder some place.&#160; Checkout the trunk folder from the repository you just created by specifying the file:///bla/bla/bla/trunk style URL:</p> <p><a href="http://lh5.google.com/jtpoll/RzuGNsVXyAI/AAAAAAAABGM/FXWCf8DAqr8/05_checkout%5B2%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="83" alt="05_checkout" src="http://lh6.google.com/jtpoll/RzuGN8VXyBI/AAAAAAAABGU/XhMyjIe87PQ/05_checkout_thumb.png" width="168" border="0" /></a></p> <p>Now, specify the svn:external properties that will link your local repository's trunk folder to multiple remote repository folders. To do this, right-click on your working folder, and choose <em>TortoiseSVN-&gt;Properties</em>. In the dialog that pops up, click the '<em>Add</em>' button. This will bring you to this dialog:</p> <p><a href="http://lh4.google.com/jtpoll/RzuGOcVXyCI/AAAAAAAABGc/3HnE4s04u1A/06_specify_externals%5B4%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="251" alt="06_specify_externals" src="http://lh5.google.com/jtpoll/RzuGOsVXyDI/AAAAAAAABGk/tF_UWnZPYzA/06_specify_externals_thumb%5B2%5D.png" width="479" border="0" /></a></p> <p>Choose '<em>svn:externals</em>' From the <em>Property name</em> combo box. In the <em>Property value</em> text box, you will need to enter key/value pairs of the remote repositories.&#160; The first item on the line is what you want folder to be named in your local repository.&#160; The second item on the line is the full path to the remote repository.&#160; (If you have credentials already cached for the remote repository, you shouldn't have a problem, otherwise you'll probably be asked to specify your username/password when you update later.)</p> <p>When you hit the &quot;<em>OK</em>&quot; button, you'll notice that your working folder has been changed:</p> <p><a href="http://lh3.google.com/jtpoll/RzuGPMVXyEI/AAAAAAAABGs/WhILMgviyjA/07_specify_externals2%5B4%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="274" alt="07_specify_externals2" src="http://lh4.google.com/jtpoll/RzuGPcVXyFI/AAAAAAAABG0/w5kq_sfxbl0/07_specify_externals2_thumb%5B2%5D.png" width="479" border="0" /></a>&#160;</p> <p>Now, commit your working folder back to your local repository.&#160; Don't worry, the repository(ies) linked via the svn:externals property will not be modified at this time.&#160; </p> <p><a href="http://lh5.google.com/jtpoll/RzuGPsVXyGI/AAAAAAAABG8/ao-CdBveyfI/08_externals_commited%5B3%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="165" alt="08_externals_commited" src="http://lh6.google.com/jtpoll/RzuGP8VXyHI/AAAAAAAABHE/BKrINomuc1o/08_externals_commited_thumb%5B1%5D.png" width="420" border="0" /></a>&#160;</p> <p>Now, update your working folder.&#160; You'll notice that all of the repository locations mentioned in your svn:externals property will be checked out to your working folder.&#160; Hooray!&#160; You did it!</p> <p><a href="http://lh4.google.com/jtpoll/RzuGQcVXyII/AAAAAAAABHM/QNE3loDCUjw/09_update_includes_externals%5B3%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="220" alt="09_update_includes_externals" src="http://lh6.google.com/jtpoll/RzuGQ8VXyJI/AAAAAAAABHU/LOHXZdhRmwg/09_update_includes_externals_thumb%5B1%5D.png" width="479" border="0" /></a> </p> <p>You are now able to work in the sections of the remote repository that you want to work on, while completely ignoring all the other cruft that you don't want to see or care about. When you commit any changes to this local repository, you'll be pushing those changes to the remote repository in a single atomic commit.&#160; </p> <p>(I must admit, I originally <a href="http://tortoisesvn.net/node/238">found this via tortoisesvn.net</a>, but since it's been one of those issues that I've dealt with for so long, I decided I had to duplicated the work-around here. )</p> <p>To further understand how this works, I must again point you to <a href="http://svnbook.red-bean.com/en/1.4/svn.advanced.externals.html">the section of the official SVN book</a> that describes externals, and what they're really intended to be used for.</p> <p>Let me know if this helps!</p> <p>&#160;</p> <h2>Update - Gotchas</h2> <p>Surprisingly, there are very few gotchas with this technique. Thankfully, the only ones I've discovered are easily worked-around. <br />So, you have multiple externals defined, and you've made changes to the various external 'parts', like so:</p> <p><a href="http://lh4.google.com/jtpoll/R1gVuudVZvI/AAAAAAAABH8/8mXk3CkYytA/10_commit_multiple_externals%5B2%5D"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="188" alt="10_commit_multiple_externals" src="http://lh5.google.com/jtpoll/R1gVu-dVZwI/AAAAAAAABIE/cBO5R4CYEIk/10_commit_multiple_externals_thumb" width="160" border="0" /></a> </p> <p>Normally, to commit those changes, you'd go to the parent folder, and choose to commit from there. The problem is, as you'll notice, is that TortoiseSVN will give you a message like: </p> <p><a href="http://lh6.google.com/jtpoll/R1gVvOdVZxI/AAAAAAAABIM/6BpbgboRn1E/11_tortoise_notification%5B3%5D"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="166" alt="11_tortoise_notification" src="http://lh4.google.com/jtpoll/R1gVvudVZyI/AAAAAAAABIU/dWc3Skz7yO4/11_tortoise_notification_thumb%5B1%5D" width="479" border="0" /></a> </p> <p>You'll also notice that the list of modified files doesn't include your changes. Notice how the message states that you'll have to commit those changes <i>separately</i>. If we'd RTFM'd like we were supposed to, we'd notice the paragraph near the bottom of <a href="http://svnbook.red-bean.com/en/1.0/ch07s03.html">Chapter 7, Section 3 of the SVN Book</a> states: <i>&quot;So, for example, if you want to commit changes that you've made in one or more of those external working copies, you must run svn commit explicitly on those working copies&#8212;committing on the primary working copy will not recurse into any external ones.&quot;</i> <br />This is exactly what we're trying to <u>not</u> do! Thankfully, there's a work-around which allows us to still create atomic commits across those externals:</p> <ol> <li>Go into the the working folder </li> <li>Select each folder that has modifications that you want to commit. </li> <li>Right-click -&gt; SVN Commit </li> <li><i>TADA</i></li> </ol> <p>Now, what we haven't ran into, but I imagine could theoretically happen is, what if one of those externally-included sources itself has external-definitions? For example, in the sample repository we've been using here, what if the DBManager had an external definition? If you made modification inside that nested external definition, there would be no way for you to commit the changes inside that nested external definition, <i>and</i> the changes in your home-spun externals repository.</p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com3tag:blogger.com,1999:blog-9236822.post-85182020805095057522007-11-01T10:33:00.001-04:002007-11-05T09:03:00.399-05:00Dell / EMC AX150i/AX150SCi and Windows Server 2003 x64 iSCSI Initiator Woes<p>Can I have a much larger post name?&#xA0; </p> <p>In summary, this post details troubles I experienced by doing the right thing: I RTFM'd, and the manual was wrong.</p> <p>I'm not sure how many people this post will help, but I know I'm writing it under the same guidelines I usually follow for posting: I had a hard time solving a problem, and the internet at large really didn't offer me much help, so, here we are.</p> <p>At work we recently purchased a new server and storage-area-network (SAN) device:</p> <ul> <li>Server: Dell 2950 <ul> <li>Dual Intel Xeon 5320's (quad-core!) (1.86Ghz, 4MB cache, 1066MHz FSB) - 8 cores! </li> <li>16GB RAM </li> <li>6 160GB hard drives - RAID5'd via the integrate PERC 5/i RAID card. (I have some gripes about the PERC 5/i that I may describe in a later post.) </li> <li>Dual integrated gigabit ethernet adapters. </li> </ul> </li> <li>SAN Device: Dell / EMC AX150i SP (ie: EMC AX150SCi) <ul> <li>iSCSI interface </li> <li>Single storage processor (ie: &quot;SP&quot; in Dell-speak, &quot;SCi&quot; in EMC-speak) </li> <li>8 250GB HD's in a RAID-5 config (with space for 4-more drives, and the ability to swap-in 750GB drives!) </li> <li>Separate 8-port Dell PowerConnect 2708 gigabit ethernet switch for the SAN network backbone / 'fabric'. </li> </ul> </li> </ul> <p>The 2950 is great, we're got it running Windows Server 2003 R2 Standard x64 Edition (this is important for later...) We have another 2950 in the office, and the only complaint I have with both of them is the server management software's inability to notify you, in any useful way, of RAID-failure events.&#xA0; This is actually a problem with the integrated PERC 5/i card, but that's not what this post is about read <a href="http://msmvps.com/blogs/onq/archive/2007/03/30/on-dell-s-perc-5-i.aspx">Eric Neale's post about the PERC 5/i</a> for more details on that gripe.</p> <p>The SAN is pretty great too, except it's setup software is giving me problems, but before we get to that, a little more information on the configuration, and steps I took to get to the problem.</p> <p>I'm going to be using the AX150 in a less-than-super redundant configuration.&#xA0; I have the PowerConnect 2708 switch configured to be the primary switch for a private network that is used only for this storage-area-network.&#xA0; The 2950 does not have an iSCSI host bus adapter (HBA), it's going to connect to the SAN with it's secondary NIC. Installing the necessary software on the Windows side of this was very easy.&#xA0; Getting that software configured to work with the AX150 wasn't so.</p> <p>I've followed EMC's instructions to-a-'T', which pretty much proceed as:</p> <ul> <li>Install the Microsoft iSCSI Initiator software</li> <li>Install the latest EMC PowerPath software </li> <li>Install the latest EMC Navisphere Server Utility</li> <li>Install the latest EMC Navisphere Initialization Utility </li> <li>...then follow the configuration steps.</li> </ul> <p>I downloaded and installed v2.0.5 of the MS Software Initiator - followed EMC's specification that I install the 'Initiator Server' and the 'Software Initiator' - but not the 'MPIO Multipathing&#xA0; Support for iSCSI.' This gave me an icon in my control panel and on my desktop.&#xA0; EMC's documentation didn't state anything about configuring it at this point, so I left it alone. (yeah, that's foreshadowing.)</p> <p>I then downloaded and installed EMC's PowerPath v5.1.0 from their registration-only support site.&#xA0; This required a reboot to finalize installation.</p> <p>Next up I downloaded and installed&#xA0; v6.20.4.2.0 of the Navisphere AX Server Utility. This is where I ran into a couple separate issues.&#xA0; In the box of the AX150 there was a big-poster-style &quot;Getting Started&quot; guide that came with the AX150.&#xA0; At the point where it says to install the Server Utility it states to install with the defaults, and to make sure that the 'Registration Server' <em>is</em> installed - which it states is the default.&#xA0; But, it was disabled by default when I ran the installer.&#xA0; The documentation I retrieved directly from EMC's web site stated that I should explicitly <em><u>not</u></em> install the 'Registration Service'.&#xA0; Okaaaaaay. So, which is correct? First time through, I choose not to install it because I figured the online documentation was more recent/up-to-date. The second snag in this installation started when it asked me: </p> <blockquote> <p>&quot;<i>Are you installing this utility on a server that is using the Microsoft iSCSI initiator to connect to the CLARiiON storage system?</i>&quot;</p> </blockquote> <p>Neither the 'big-poster' or the online documentation stated what I should choose, so, since it sounded appropriate, I choose 'Yes'. It then presented this message/error:</p> <blockquote> <p>&quot;<em>Microsoft Initiator is needed to setup your iSCSI devices. InstallShield does not find Microsoft Initiator installed on your computer. Please download Microsoft Initiator from </em><a href="http://www.microsoft.com"><em>www.microsoft.com</em></a><em> and install it.</em>&quot;</p> </blockquote> <p>Whaaaaaaaat?&#xA0; I had already installed MS iSCSI Initiator!&#xA0; Strangely though, the installation didn't abort, and continued on to completion with no other errors or strange messages.</p> <p>After this, I download and installed v6.20.0.3.11 of the Navisphere Initialization Tool. After this is installed, I was instructed to use it to initialize the AX150. The initialization process assigns IP addresses to the management interface and iSCSI ports on the unit.&#xA0; </p> <p>After initialization, I was instructed to open the Server Utility and choose the &quot;<strong><em>Configure iSCSI Connections on this server</em></strong>&quot; option. Well, look at the screenshot below and you tell me what I should choose:</p> <p><a href="http://lh6.google.com/jtpoll/Ryo-GMdLieI/AAAAAAAABEI/jSFirME7etg/emc_server_utility%5B7%5D.png"><img id="id" style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="334" alt="emc_server_utility" src="http://lh4.google.com/jtpoll/Ryo-GsdLifI/AAAAAAAABEQ/IHRwjl6yMqU/emc_server_utility_thumb%5B5%5D.png" width="474" border="0" /></a>&#xA0;</p> <p>Yup.&#xA0; There's my problem summed up in a screenshot.&#xA0; Figuring I had missed something, or done something wrong, I meticulously combed through my installation notes, and cross-referenced the installation documentation. I tried rolling back installations and re-installing with different options. (Specifically, I tried installing the 'Registration Service', and I also tried answering 'no' to the Server Utilities install question mentioned earlier.)&#xA0; No combination of installation options made a difference. I spelunked through more documentation on EMC's support site.&#xA0; I looked on Dell's site for documentation -- they kept pointing me to EMC's support site.&#xA0; I tried Google for answers - I found nothing.&#xA0; </p> <p>I finally decided I'd have to get in touch with Dell.&#xA0; I went to their support page, got to their 'online chat support' area, entered the AX150's service-tag, and, tada, found that everyone was busy with someone else.&#xA0; (It's nice to see chat-support is no-more available than phone support.)&#xA0; I tried a couple times, hoping to get through.&#xA0; Nothing.&#xA0; I really didn't want to email them because I needed to get this system online, and who knows how many days turn-around it would take to get resolved via email.&#xA0; </p> <p>I started looking for the correct phone number to call, and I somehow, probably because I'd entered the AX150's service tag, I found myself at a tech-support page specific to the AX150.&#xA0; There's a section near the top: &quot;<em>Top Solutions : Frequently Asked Questions About Your Model</em>&quot;:</p> <ol> <li><em>What is RAID 0 and RAID 1</em></li> <li><em>What is RAID 5</em></li> <li><em>How can I collect the SP event logs from a CLARiiON FC4700 or CS-Series array using the serial port?</em></li> <li><em>How do I configure the LUNs on my DELL|EMC&#xAE; array?</em></li> <li><a href="http://support.dell.com/support/topics/global.aspx/support/dsn/en/document?docid=3073946DB8788D79E040A68F5B286BEB"><em><u>EMC&#xAE; Navisphere&#xAE; Server Utility iSCSI configuration is not available in Microsoft&#xAE; Windows&#xAE; 2003 64-bit</u></em></a></li> </ol> <p>(Side question: What's with questions #1 and #2?&#xA0; &quot;You must be at least this smart to ride this ride.&quot; comes to mind. hehe.)</p> <p>Number five!&#xA0; Look at that!&#xA0; That's exactly my problem! Dell's documentation had the answer, not EMC's!&#xA0; Dell keeps redirecting you to EMC's documentation when they had the answer to my question all along.&#xA0; </p> <p>It's a bummer that I had to spend this much time finding this answer because getting things configured manually through the MS iSCSI Initiator is really quite simple.</p> <p>Oh well...live-n-learn. </p> Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com2tag:blogger.com,1999:blog-9236822.post-21207438016511595582007-10-28T16:43:00.000-04:002007-11-18T20:24:11.980-05:00Getting Breakpoints to work in Eclipse PDT using Zend DebuggerNovember 18th, 2007 UPDATE: Please see the <a href="#update">section at the bottom of this post</a> for an update!<br /><br />I've recently been fooling around with PHP.<br /><br />At work we have a <a href="http://www.blogger.com/drupal.org">Drupal</a>-based web site, and I figured it was time to get a better mental-grasp around PHP. After looking through a whitepaper from IBM titled <a href="http://www.ibm.com/developerworks/ibm/osource/index.html">Using open source software to design, develop, and deploy a collaborative Web site</a> I decided I needed to get myself a development environment setup in which I could work with Drupal 'under-the-hood'. <br /><br />IBM's whitepaper was written in various pieces over the course of about a years time. One of the sections discusses setting up <a href="http://www.blogger.com/www.eclipse.org">Eclipse</a> with different plugins that facilitate PHP development. In a later section they mention the <a href="http://www.eclipse.org/pdt/">PHP Development Tools (PDT)</a> - a more comprehensive set of PHP development and debugging tools for Eclipse. Lucky for me, they now have a PDT all-in-one Eclipse package that makes setup even easier. (Get the package from <a href="http://download.eclipse.org/tools/pdt/downloads/">here</a>.) After downloading and installing it, and getting a feel for the whole thing (wow, Eclipse is complicated at first,) I got down to the brass-tacks and tried debugging.<br /><br />Before I go on, here's a little more information about my development environment:<br /><ul><li>OS: Window XP SP2<br /></li><li>Development Environment:<br /> <ul><li>Eclipse : PDT<br /> </li><li>PHP 5.2.4<br /> </li><li>MySQL 5.Something<br /></li></ul></li><li>Web server: Apache<br /></li></ul>PHP, MySQL and Apache are all from the <a href="http://www.apachefriends.org/en/xampp.html">Apache Friend's XAMPP package.</a><br /><br />I followed the instructions on the PDT Wiki for instructions on how to <a href="http://www.thierryb.net/pdtwiki/index.php?title=Using_PDT_:_Installation_:_Installing_the_Zend_Debugger">Install the Zend Debugger</a> for Eclipse.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qt9sjR3SvlA/RyTdB8dLiaI/AAAAAAAABDo/JM1sjo20M1g/s1600-h/bad_pdt_debug_config.png"><img style="cursor: pointer;" src="http://1.bp.blogspot.com/_qt9sjR3SvlA/RyTdB8dLiaI/AAAAAAAABDo/JM1sjo20M1g/s400/bad_pdt_debug_config.png" alt="" id="BLOGGER_PHOTO_ID_5126465301332593058" border="0" /></a><br /><br />Just for reference, in the screenshot above, you'll notice my "Dummy Debugger Test Server" configuration. Here's a screenshot of the specific details of that config:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qt9sjR3SvlA/RyTfZcdLibI/AAAAAAAABDw/NMNNqUhx4lI/s1600-h/bad_pdt_server_debug_config.png"><img style="cursor: pointer;" src="http://3.bp.blogspot.com/_qt9sjR3SvlA/RyTfZcdLibI/AAAAAAAABDw/NMNNqUhx4lI/s400/bad_pdt_server_debug_config.png" alt="" id="BLOGGER_PHOTO_ID_5126467904082774450" border="0" /></a><br /><br />After getting everything setup, I could get the debugger to debug any PHP-based web page -- so long as I had the "Break at First Line" debugging option set. I could start the debugger and at the first line of PHP code, the debugger would 'catch', and allow me to step through my code. Everything worked as expected - variables and their values were visible in the Variables dialog, I could step into and out of various functions. Unfortunately, if I set breakpoints anywhere, they would never 'hit', the debugger wouldn't halt the PHP execution on the specified breakpoints. It would zip right past them as though they never existed.<br /><br />Naturally I hit-up my good friend Google for answers. I found a number of people having the same problem as myself. I found instances of people having this problem all the way back in January of 2007. I also found instances of people having this problem as recently as October of 2007! Every single time I found someone saying they had the problem, nobody ever had an answer for them! <br /><br />Finally, <span style="font-style: italic;">finally</span>, I was able to find <a href="http://dev.eclipse.org/newslists/news.eclipse.tools.pdt/msg01345.html">this</a> somewhat cryptic post on how someone fixed the problem:<br /><br /><blockquote><br />Not sure if this helps, but I ran into the same problem myself. I found out that it was that the plugin expects the project root to be the http root.<br /><br />My project structure had the http root as a subfolder of the project root.<br /><br />If the http root isn't the same point as the project root, then the plugin can't correctly inform the debugger which file the breakpoint is in.<br /><br />I ended up hacking the source to make the plugin work at a subfolder level.<br /></blockquote><br /><br />Problem was, he stated that he hacked the source of the plugin to 'make the fix'. Not exactly what I was hoping to do. I figured I was approaching yet another dead end, but something about him stating that the plugin was simply confused about expecting your Eclipse project's root to be the web server's root gave me an idea: Move my content to the virtual host's root directory.<br /><br />I tried moving my project's files all into the root folder of the web server (just dummy.php in this case.) I modified my "Dummy Debugger Test Server" configuration as shown in this screenshot:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qt9sjR3SvlA/RyTyGsdLicI/AAAAAAAABD4/V-5HmNr77E4/s1600-h/good_pdt_server_debug_config.png"><img style="cursor: pointer;" src="http://4.bp.blogspot.com/_qt9sjR3SvlA/RyTyGsdLicI/AAAAAAAABD4/V-5HmNr77E4/s400/good_pdt_server_debug_config.png" alt="" id="BLOGGER_PHOTO_ID_5126488472681155010" border="0" /></a><br /><br />I made sure that "Break at First Line" was unchecked/disabled, and fire up the debugger. <u>It worked.</u> It finally worked. After all that trouble it freakin' worked. <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qt9sjR3SvlA/RyT45sdLidI/AAAAAAAABEA/OLU3cF6kB5A/s1600-h/pdt_breakpoints_working.png"><img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_qt9sjR3SvlA/RyT45sdLidI/AAAAAAAABEA/OLU3cF6kB5A/s400/pdt_breakpoints_working.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5126495945924250066" /></a><br /><br /><br />Has anyone else had this much trouble? Has anyone been able to get debugging to work when your debugged PHP doesn't reside in the root of your web server?<br /><br /><a name="update">November 18th, 2007 Update:</a><br />I think I may have a better example. I've also been 'playing' around with the <a target="_new" href="http://www.symfony-project.com/">Symfony PHP Framework</a>. I again ran into this breakpoints-not-working problem when trying to debug a Symfony-based project. <br /><br />I'm going through the <a href="http://www.symfony-project.com/askeet/1_0/">"Askeet" tutorial</a> and have imported a project into Eclipse and had my Apache instance serving up the "/web" subfolder served up as the server's document root.<br /><br />Of course, this confuses the debugger plugin, because, again, the debugger is assuming that the Eclipse project-root corresponds directly to the web server's root directory, which it's not. This is the situation: <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qt9sjR3SvlA/R0DjIMVXyKI/AAAAAAAABH0/74H6JzBM2sc/s1600-h/broken_debugger_diagram.png"><img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_qt9sjR3SvlA/R0DjIMVXyKI/AAAAAAAABH0/74H6JzBM2sc/s400/broken_debugger_diagram.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5134353305091688610" /></a><br /><br />The arrow titled "Assumed" is what the debugger is wrongly assuming. To fix the problem, you have to configure your web server to match that assumption.<br /><br />So, I modified the virtual host's DocumentRoot to point to the Askeet project's root folder, even though there is no index.html or index.php to be served from there. I had to also modify the "/sf" alias to handle "/web/sf". (If you're using Symfony, you should know what I'm talking about.) So, now instead of going to http://localhost, I have to go to http://localhost/web/. But since this is strictly for development purposes, it's not a big deal. Oh, and I had to make a slight modification to the debugging profile in Eclipse to match the new /web/index.php URL. Again, not a big deal.<br /><br />I hope this update cleared any confusion...Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com32tag:blogger.com,1999:blog-9236822.post-82870902907481836542007-09-27T12:21:00.000-04:002007-09-27T13:14:08.053-04:00TortoiseSVN's "Noisy" TSVNCache.exeA co-worker of mine pointed me to a post by Travis Illig on <a href="http://paraesthesia.com">his blog</a> titled <a href="http://paraesthesia.com/archive/2007/09/26/optimize-tortoise-svn-cache-tsvncache.exe-disk-io.aspx">"Optimize Tortoise SVN Cache (TSVNCache.exe) Disk I/O"</a><br /><br />A short synopsis:<br /><br /><div style="border: 2px solid rgb(0,0,0); margin: 15px; padding: 25px;"><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qt9sjR3SvlA/RvvdWkEnbbI/AAAAAAAAAk0/UOPl9TyJPPs/s1600-h/overlay.png"><img style="float:left; padding: 0 10px; cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_qt9sjR3SvlA/RvvdWkEnbbI/AAAAAAAAAk0/UOPl9TyJPPs/s400/overlay.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5114925181519228338" /></a><br />TortoiseSVN has a shell-overlay that indicates the status of your files and folders within an SVN 'working folder'. <br /><br />The background process that 'watches' your file system for SVN-related files and folders is TSVNCache.exe. <br /><br />TSVNCache.exe, according to Travis, was bogging his system down with disk I/O.<br /><br />Instead of disabling TSVNCache.exe entirely (and thereby disabling the icon-overlays) he found a way to limit what TSVNCache.exe 'watches' on the file system.</div><br /><br />When I first read his post, I thought "How bad can TSVNCache.exe be?" So, as noted in the comments of Travis' post, I grabbed <a href="http://www.microsoft.com/technet/sysinternals/FileAndDisk/Filemon.mspx">FileMon</a> from SysInternals, and fired it up. After setting a filter so it would only display TSVNCache.exe-related events, I was amazed at just how much 'noise' it was causing -- and I wasn't doing anything at all! Just sitting there watching FileMon do it's thing.<br /><br />What really caught my eye was when I sent my coworker an instant message, thanking him for the link. My IM client wrote the message to it's log file, and I saw TSVNCache note the change, and check the log file's location for the existence of a ".svn" folder. (The existence of such a folder would indicate that the folder is an SVN working folder.)<br /><br />I was surprised to say the least, and wanted to see how bad it really got. I 'putzed ' around doing a whole lot of nothing other than navigating around my file system. I seemed ridiculous at just how much disk I/O TSVNCache.exe was inducing. <br /><br />For example, look at the following screen shot. It's the TSVNCache activity imposed when I copied a folder on my desktop, which contains <b>one file</b>:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qt9sjR3SvlA/RvvgGEEnbcI/AAAAAAAAAk8/rfN4c4YrDxI/s1600-h/filemon.png"><img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qt9sjR3SvlA/RvvgGEEnbcI/AAAAAAAAAk8/rfN4c4YrDxI/s400/filemon.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5114928196586270146" /></a><br /><br />Thankfully, by limiting which folders TSVNCache.exe 'watches' you can greatly limit this superfluous disk I/O. In case you still haven't taken a moment to click-through and read Travis' post, here's the quick run-down:<br /><br /><ol><br /><li>Open your TortoiseSVN settings (Right-click the desktop, TortoiseSVN -> Settings)</li><br /><li>Go to the "Icon Overlays" item in the treeview.</li><br /><li>Add "C:\*" to the "Exclude Paths" box</li><br /><li>Add the folders you do want included in the "Include Paths" box<br /><ul><li>I entered my 'work' folder: "C:\Work\*", for example.</li></ul></li><br /><li>Save changes : Apply/Ok</li><br /><li>Restart TSVNCache.exe - kill TSVNCache.exe in your Task Manager. It will automatically restart when you open a Windows Explorer.</li><br /></ol><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qt9sjR3SvlA/RvvimkEnbdI/AAAAAAAAAlE/tS4qxwkfDOg/s1600-h/tsvnsettings.png"><img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_qt9sjR3SvlA/RvvimkEnbdI/AAAAAAAAAlE/tS4qxwkfDOg/s400/tsvnsettings.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5114930953955274194" /></a><br /><br />With these changes in place, within FileMon, you'll notice that TSVNCache is a <i>lot</i> less noisy. It will still receive "Change Notify" messages, but you won't see it access files/folders outside of your specified "Include" list.<br /><br /><br />Thanks for the great tip, Travis!Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com13tag:blogger.com,1999:blog-9236822.post-30001168167650918822007-09-18T11:16:00.000-04:002007-09-27T15:22:11.620-04:00Diff / Merge Tools<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qt9sjR3SvlA/Ru_0Nzt_rbI/AAAAAAAAAj8/FUtXqVAL2ek/s1600-h/tortoisemerge.png"><img style="float:right;" src="http://4.bp.blogspot.com/_qt9sjR3SvlA/Ru_0Nzt_rbI/AAAAAAAAAj8/FUtXqVAL2ek/s400/tortoisemerge.png" alt="" id="BLOGGER_PHOTO_ID_5111572620147404210" border="0" /></a><br />I've been using <a href="http://tortoisesvn.tigris.org/TortoiseMerge.html">TortoiseMerge</a> (part of <a href="http://tortoisesvn.net/">TortoiseSVN</a>) for a while now to view my source's diff's as well as handling merge conflicts. Something about it though has always felt 'clunky' to me. It may just be it's default color scheme that causes my mental detraction, but when I saw <a href="http://www.larkware.com/">Larkware</a> plug something call <a href="http://www.larkware.com/dg9/TheDailyGrind1228.aspx">DiffMerge 3.1 beta</a> I figured I should give it a try.<br /><br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qt9sjR3SvlA/Ru_1Bzt_rdI/AAAAAAAAAkM/aS-OJFwgBk4/s1600-h/diffmerge.png"><img style="float: left;" src="http://4.bp.blogspot.com/_qt9sjR3SvlA/Ru_1Bzt_rdI/AAAAAAAAAkM/aS-OJFwgBk4/s400/diffmerge.png" alt="" id="BLOGGER_PHOTO_ID_5111573513500601810" border="0" /></a>So far, I like it. It's UI is more sexy, as far as I'm concerned. It supports 3-way merging (like TortoiseMerge), making life so much easier when resolving merge conflicts. It supposedly supports folder diff/merging, although I haven't tried that yet.<br /><br />I just wish I hadn't also accidentally stumbled upon another free diff/merge tool called <a href="http://kdiff3.sourceforge.net/">KDiff3</a>. I like KDiff3's shell context menu for it's ability to 'remember' a file's location on the file system. Once a file is 'remembered', you can perform a diff on any other file on the system by asking KDiff3 to compare the current file you have in front of you, and one of the 'remembered' files from earlier. (I've found myself needing to do this lately and KDiff3 definitely speeds up the process.)<br /><br />One other thing I liked about KDiff3 was in it's installer: It provided an install option to integrate with TortoiseSVN as Tortoise's primary diff/merge tool.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qt9sjR3SvlA/Ru_23Dt_reI/AAAAAAAAAkU/n-I_6qZPo5A/s1600-h/kdiff3.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://1.bp.blogspot.com/_qt9sjR3SvlA/Ru_23Dt_reI/AAAAAAAAAkU/n-I_6qZPo5A/s400/kdiff3.png" alt="" id="BLOGGER_PHOTO_ID_5111575527840263650" border="0" /></a>The only problem with KDiff3 was, it's color scheme made me throw up in my mouth so fast that I quickly decided I had to give DiffMerge another shot. (The screenshot shown here doesn't truly show off the vomit-in-mouth colors so much...) KDiff's ability to integrate easily with TortoiseSVN got me looking into how to set DiffMerge as my Tortoise diff/merge tool.<br /><br />Being perpetually low on free time, and not wanting to figure out the command line options myself, I enlisted Google to give me answers. In my search for integrating DiffMerge as my TortoiseSVN diff/merge tool, I ran into <a href="http://dotnet.org.za/trumpi/archive/2007/06/05/sourcegear-release-diffmerge-for-free.aspx">this post by 'Trumpi' detailing</a> exactly how to integrate the two! So, instead of outright stealing from Trumpi, I give you: <a href="http://dotnet.org.za/trumpi/archive/2007/06/05/sourcegear-release-diffmerge-for-free.aspx">Trumpi's blog : SourceGear release DiffMerge</a>.<br /><br />...<br />Ok, I'll steal a little, only because I tweaked my settings slightly for the diff-tool specification (caught the tweak in Trumpi's post comments). Here's the command lines for specifying DiffMerge as your diff and merge tools for TortoiseSVN:<br /><ul><li>Diff:<br /><ul style="font-family: courier new;"><li>New way: (see comments)<br>C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe /ro2 /t1=%bname /t2=%yname %base %mine </li><li>Old way:<br><s>C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe /t1=Base /t2=Mine %base %mine</s></li></ul></li><br /><li>Merge:<br /><ul style="font-family: courier new;"><li>New Way: (see comments)<br> C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe /r=%merged /t1=%yname /t2=%bname /t3=%tname /c=%mname %mine %base %theirs</li><li>Old Way:<br><s>C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe /t1=Mine /t2=Base /t3=Theirs /r=%merged %mine %base %theirs</s></li></ul></li><br /></ul><br /><br /><b>Update:</b><br><br />Note on the updated TortoiseSVN external tool commands: In the comments, Travis mentioned that I should RTFM. Upon inspecting the <a href="http://download.sourcegear.com/DiffMerge/3.1-beta/DiffMergeManual.pdf">DiffMerge manual</a>, I came up with the updated command lines above. I especially appreciate the "/ro2" option for the diff-tool. This makes DiffMerge act strictly like a diff-tool (ie: no editing allowed.)<br /><br />Also, I gave Jimmy's <a href="http://winmerge.org/">WinMerge </a>a try. It does fit my 'eye-candy' requirement, but I just can't devote more time to it because of it's lack of 3-way merging.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com8tag:blogger.com,1999:blog-9236822.post-71530598263816452682007-09-05T10:54:00.000-04:002007-09-20T14:28:08.968-04:00Google Earth updateHey, did anyone else notice the cool additions they made in Google Earth?<br /><br /><a href="http://earth.google.com/sky/skyedu.html">Check Out The Details Here.</a><br /><br />Of special note, you can now flip-around and view the sky from your current location on Google Earth! This is really cool. There is also a <a href="http://earth.google.com/intl/en/userguide/v4/flightsim/index.html">built-in flight simulator</a>. It's pretty easy to crash in the flight sim, so, I didn't really play much with it, just the 'view sky' mode.<br /><br />Anyway... Quick post!Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-12573150982055323932007-08-27T16:48:00.000-04:002007-08-27T16:50:02.873-04:00Cool SIGGRAPH 'Content Aware' Image Resizing DemonstrationThis is pretty stinkin' cool:<br /><br /><div class="video"><br /> <object width="500" height="393"><param name="movie" value="http://www.youtube.com/watch?v=qadw0BRKeMk"></param><embed src="http://www.youtube.com/v/qadw0BRKeMk" type="application/x-shockwave-flash" width="500" height="393"></embed></object><br /> </div>Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com3tag:blogger.com,1999:blog-9236822.post-22380439395068694602007-07-26T22:23:00.000-04:002007-07-26T22:23:28.395-04:00I Have Adam's Book!Just wanted to say that I got Adam Nathan's COM Interp book, and I'm already taking notes as to what I need to blog about. :) It's already answering questions that I've had, and while I thought what I was recently reading was going to give insight into the problems I had previously, it just skirted the issue and said "More in Chapter 7, 20, and 24".<br /><br />This book is <em>huge</em>. We're talking bigger-than-the-Bible huge. So big, they wouldn't bind it with just one binding. No, they bound it as two seperate tomes. Tomes of arcane COM knowledge. ;) <br /><br />Although I'm only into chapter 4, I can tell this book has what I need. Heh, well, I've also heard that if this book doesn't have the answers to your COM-interop questions, then no other book will, soooo. :) It hasn't been updated for .NET 2.0/3.0/3.5. I'm curious to know if any of the version-specific remarks Adam makes regarding the implementation of the SDK at the time of his writing have changed. I would guess the answer is "not much" because who would miss the chance to write a second edition!? :D<br /><br />Anyway, before I go, does anyone else think it's kinda weird the way I just happened to have "N" post-it notes for Adam *N*athan's book? <A HREF='http://4.bp.blogspot.com/_qt9sjR3SvlA/RqlXH9PBIwI/AAAAAAAAAiQ/20gGVLk70co/s1600-h/100_6399.JPG'><IMG SRC='http://4.bp.blogspot.com/_qt9sjR3SvlA/RqlXH9PBIwI/AAAAAAAAAiQ/20gGVLk70co/s320/100_6399.JPG' border=0 alt='' id='BLOGGER_PHOTO_ID_' style='clear:both;float:right; margin:0 0 10px 10px;'></A>&nbsp;<div style='clear:both; text-align:RIGHT'><a href='http://picasa.google.com/blogger/' target='ext'><img src='http://photos1.blogger.com/pbp.gif' alt='Posted by Picasa' style='border: 0px none ; padding: 0px; background: transparent none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial;' align='middle' border='0' /></a></div>Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-81033400595335232612007-07-18T16:35:00.000-04:002007-09-13T13:28:24.499-04:00VMWare : Loosing eth0 after you've copied your VM<div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 4px; vertical-align: middle; background-color: #FFF7C0;">Background</div><br />Here's a bit of weird-behavior I've noticed when working with some of our production virtual machines (running <a href="http://www.gentoo.org/">Gentoo</a> Linux) here at work.<br /><br />In order to update the OS's on our virtual machines, I will copy them to my local machine, power them on, update them, and then push the updated OS's back out into production at the earliest convenience.<br /><br />When you copy the VM from one location to another, VMWare notices this and asks you "Hey, it looks like this machine has been physically moved or copied, do you want me to create a new VM-UUID?" If you answer in the affirmative, VMWare internally regenerates any unique-identifiers tied to this virtual machine. The one thing that's really noticeable is that any virtual ethernet adapters get their MAC addresses changed.<br /><br />The problem I've experienced is that when you power on the new-UUID'd VM, you no longer have an ethernet adapter. Gentoo tries to bring-up eth0 and it says "network interface eth0 does not exist" and "Please verify hardware or kernel module (driver)" <br /><br /><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 4px; vertical-align: middle; background-color: #FFF7C0;">Explanation</div><br /><i>"So, what's going on?"</i><br /><br />Try a couple things:<br /><ul><li>If you run <code>lspci</code> you should still see the ethernet adapter.</li><br /><li>If you run '<code>dmesg</code>' and should see the kernel find the network card and it even calls it <code>eth0</code></li></ul><br /><br /><i>"So, where does <code>eth0</code> go?"</i><br /><br />Try running <code>ifconfig -a</code>. I <i>bet</i> you now have an <code>eth1</code> and it's MAC address matches the newly-generated virtual MAC address specified in the virtual machine's <code>.vmx</code> file. <br /><br /><i>"Oh great, so every time I copy the VM I need to update the system configs to use the new eth1, or eth2, etc!?!?!"</i><br /><br />No, hush, I'm getting to the answer.<br /><br />The problem stems from the linux distro 'remembering' the MAC address of the network adapter and expecting it to be the same between boots. In the case of our Gentoo VM's, it's <code>udev</code> that mucks this up.<br /><br /><i>"Ok, fine, it's udev's fault. We know it's broken because it's expecting the ethernet adapter to have a MAC address that it no longer has. What to do?</i><br /><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 4px; vertical-align: middle; background-color: #FFF7C0;">The Answer</div><br />To fix this problem you need to tell your linux distro the VM's new MAC address. How you do this can vary by distro. In my spelunking, I found a few ways:<br /><br /><ul><li>In Gentoo do one of the following: (Do #1, it's the easiest.) <br /> <ol><li>Delete <code>/etc/udev/rules.d/70-persistent-net.rules</code> and reboot. <u>Your eth0 should be back.</u><ul><li><b>2007/09/13 Update</b>: This almost-always works for me. But, for some reason, sometimes it seems to confuse udev even more; after rebooting, I'll have an eth2 or eth3. When this happens, I end up following #2, making sure the udev config file has 'eth0' listed, and not eth1, eth2, or eth3.</li></ul></li><li>Edit <code>/etc/udev/rules.d/70-persistent-net.rules</code> (or whatever it's named) to match your new MAC address and reboot. <u>Your <code>eth0</code> should be back.</u></li></ol></li><li>Other distros:<ul><li>Look for, (and edit if you find,) <code>/etc/iftab</code></li><li>Look for, and delete, then reboot <code>/etc/udev/rules.d/25-iftab.rules</code></li><li>Look for, (and edit if you find,) <code>/etc/sysconfig/network-scripts/ifcfg-eth0</code></li></ul></li></ul><br /><br /><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 4px; vertical-align: middle; background-color: #FFF7C0;">Give Credit Where Credit Is Due</div><br />I got hints from a number of pages, but in the end, it was the folks over at the VMWare discussion forums for the win:<br /><a href="http://www.vmware.com/community/thread.jspa?threadID=46069&amp;tstart=0" class="externallink" title="VMWare Discussion Forums" target="_blank">VMWare Discussion Forums</a>Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com32tag:blogger.com,1999:blog-9236822.post-89280400231243545492007-07-13T08:35:00.000-04:002007-07-13T11:13:31.096-04:00Marshalling Arrays To VB6's COM Funland pt2Ok, <a href="http://yoopergeek.blogspot.com/2007/07/marshalling-arrays-to-vb6s-com-funland.html">yesterday </a> I figured out how to get an array of interfaces (actually, objects that implement an interface, but go along with my sloppy grammer, ok?) out of C#, through the COM Callable Wrapper (CCW) and into COM-land. <br /><br />Wouldn't you know it, but I also need to be able to pass an array of objects back into C# from COM-land. <br /><br />Well, I'm here to say that figuring this out was a lot easier than yesterday's problem. At first I tried something like:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface ITest {<br />&nbsp;&nbsp;&nbsp;void TakeBusinessObjects(IBusinessObject[] bos);<br />}<br /></span><br /></div><br /><br />Well, that doesn't work. In VB6, when you try to compile something that calls <span style="font-family: courier new,sans-serif;">TakeBusinessObjects(...)</span> you get the a compile error like the following:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic<br /></span><br /></div><br /><br />Well, my Google digging on that particular error actually proved fruitful and the answer is simple:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface ITest {<br />&nbsp;&nbsp;&nbsp;void TakeBusinessObjects(<span style="font-weight:bold;">ref </span>IBusinessObject[] bos);<br />}<br /></span><br /></div><br /><font size="-1">(credit goes to Jon Wojtowicz for his <a href="http://www.eggheadcafe.com/articles/20051019.asp">Using COM Callable Wrappers to Extend Existing Visual Basic 6.0 Applications</a> post at <a href="http://www.eggheadcafe.com/">EggHeadCafe</a>.</font>)<br /><br />Hooray! That works! The array gets passed back into .NET-land, and things seem happy. Except, I wouldn't be writing this post if I didn't have a problem, right? Well, <span style="font-family: courier new,sans-serif;">TakeBusinessObjects(...)</span> doesn't throw an exception, and it reaches it's <span style="font-family: courier new,sans-serif;">return</span> statement successfully. Unfortunately, something gets lost in translation while returning control to VB6-land because I intermediately upon returning, VB6 raises this error:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Class does not support Automation or does not support expected interface<br />Number: 430<br /></span><br /></div><br /><br />This error makes it sound like I've developed on v2 of some COM component, but I've deployed the compiled EXE on a machine that only has v1 of the COM component. What I don't understand is that the array gets passed through the CCW into .NET-land! It works! Something just goes wrong on the way back to VB-land.<br /><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 36px; vertical-align: middle; background-color: #FFF7C0;">Update: The Answer! (Kind of)</div><br /><br />I gotta hand it to my buddy <a href="http://mrdotnet.wordpress.com/">Jimmy</a> - that guy has given me the "Try XYZ" that has fixed whatever problem I was tackling so many times -- and he's come through once again! He suggested that, I take a look at the <span style="font-family: courier new,sans-serif;">[In]</span> and <span style="font-family: courier new,sans-serif;">[Out]</span> attributes. <br /><br />By default, when I compile my COM component, it's being assumed that I not only want to be able to take in an array reference, but that I also want to push any changes made to that array reference back out to the caller. Well, lucky for me, I don't make any changes to the array once it's in .NET-land, and I can flag the parameter as only needing to come into the method, and not out. Like this:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface ITest {<br />&nbsp;&nbsp;&nbsp;void TakeBusinessObjects(<span style="font-weight:bold;">[In]</span> ref IBusinessObject[] bos);<br />}<br /><br /></div><br /><br />So, while this fixes most of the situations where I would need to pass an array from COM-land into .NET-land, it still irks me that I don't know why VB6 throws that error if the CCW marshaller tries to move the array back out of .NET-land when the method returns.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com0tag:blogger.com,1999:blog-9236822.post-85922537912952755312007-07-11T17:10:00.000-04:002007-07-13T09:05:26.124-04:00Marshalling Arrays To VB6's COM FunlandOk, so, please, don't ask why I've found myself writing a COM component in C#, and am using it in VB6. Just accept that fact that I need to.<br /><br />This COM component needs to return an array of data to VB6. At first I thought "Why not just have my C# project reference the VB6 runtime via interop, and I'll be able to instantiate a VB6 Collection class, and return that to VB6, and it will be happy.<br /><br />But no, when I try to instantiate a VBA.CollectionClass I get:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Retrieving the COM class factory for component with CLSID {A4C4671C-499F-101B-BB78-00AA00383CBB} failed due to the following error: 80040154.</span><br /><br /></div><br /><br />After much googling, the closest answer I could get was that I was trying to instantiate a VBA.Collection on an x64 platform. Um, the last time I checked my Pentium-D was a 32 bit processor. I even went so far as to force my C# COM component to compile to x86 specifically instead of the <span style="font-style:italic;">'Any CPU'</span> configuration. Still, the error persisted. <br /><br />So, I had to bail on that idea, and come up with <span style="font-style:italic;">"Hey, what about just returning an array?</span> What a great idea! I performed a test. <br /><br />I created a test COM visible interface and class, something like:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br /><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface ITest {<br />&nbsp;&nbsp;&nbsp;String[] GetStrings();<br />}<br /><br />[ClassInterface(ClassInterfaceType.None)] <br />[ComVisible(true)]<br />public class Test : ITest {<br />&nbsp;&nbsp;&nbsp;public String[] GetStrings() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List<string> foo = new List<string>();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.Add("hello");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.Add("bye-bye");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return foo.ToArray();<br />&nbsp;&nbsp;&nbsp;}<br />}<br /></div><br /><br />Then, the corresponding test-code in VB6:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Dim testObject as Test<br />Dim strings() as String<br /><br />set testObject = new Test<br />strings = testObject.GetStrings()<br /><br /></div><br /><br /><span style="font-weight:bold;">It worked.</span><br /><br />I was happy, and I continued working on my C# code as planned.<br /><br /><br />Days later, I have hundreds of lines of code down in C#, and I'm at a good point to test what I've written. Now, my objects weren't going to be returning string-arrays like the test case above, rather, they're going to be returning arrays of an interface defined in my C# COM component, something like:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface IBusinessObject {<br />&nbsp;&nbsp;&nbsp;Int32 GetSomething();<br />}<br /><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface IFoo {<br />&nbsp;&nbsp;&nbsp;IBusinessObject[] GetBusinessObjects();<br />}<br /><br /><br /><br />[ClassInterface(ClassInterfaceType.None)] <br />[ComVisible(true)]<br />public class BusinessObject : IBusinessObject {<br />&nbsp;&nbsp;&nbsp;/* you get the idea */<br />}<br /><br />[ClassInterface(ClassInterfaceType.None)] <br />[ComVisible(true)]<br />public class Foo : IFoo {<br />&nbsp;&nbsp;&nbsp;public IBusinessObject[] GetBusinessObjects() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List<IBusinessObject> bos = new List<IBusinessObject>();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* Add some BusinessObjects to 'bos' */<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return bos.ToArray();<br />&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></div><br /><br />Then, the corresponding VB6 code:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Dim foo as Foo<br />Dim myObjects() as BusinessObject<br /><br />set foo = new Foo<br />myObjects = foo.GetBusinessObjects()<br /><br /></div><br /><br /><br />Unfortunately, it didn't work. While no exception was thrown in C#, somewhere between <span style="font-family: courier new,sans-serif;">return bos.ToArray()</span> and VB6's assignment to the <span style="font-family: courier new,sans-serif;">myObjects</span> array a "<span style="font-family: courier new,sans-serif;">Type Mismatch</span>" error was thrown in VB6. I couldn't figure out why, though.<br /><br />I tried catching the array returned from <span style="font-family: courier new,sans-serif;">.GetBusinessObjects()</span> into a Variant like: <br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Dim myObjects as Variant<br />myObjects = foo.GetBusinessObjects()<br /><br /></div><br /><br />I still received the "<span style="font-family: courier new,sans-serif;">Type Mismatch</span>" error. I was really lost here, because in VB6-land a Variant is the closest thing you're going to get to a generic object pointer as you're going to get. <br /><br />I again didn't garner much assistance from another thorough Google spelunking. In my searches, I did stumble across the <span style="font-family: courier new,sans-serif;">MarshalAs</span> attribute, but since I'm not a COM-master I wasn't entirely sure what I should be marshaling an array of interfaces as in order to safely reach COM-world. I blindly tried a number of things, and always ended up with "<span style="font-family: courier new,sans-serif;">Type Mismatch</span>". I was loosing hope. (As a side note I desperately need to get a copy of <a href="http://blogs.msdn.com/Adam_Nathan">Adam Nathan</a>'s book <a href="http://www.amazon.com/NET-COM-Complete-Interoperability-Guide/dp/067232170X/ref=pd_ybh_11/104-9454856-8721520?pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=17N3B18VR3EKE2M9ZZGB&pf_rd_t=1501&pf_rd_p=280800601&pf_rd_i=ybh">.NET and COM: The Complete Interoperability Guide</a>.)<br /><br />Finally, I stumbled upon it, and I'm not entirely sure why I didn't try it first:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 36px; vertical-align: middle; background-color: #FFF7C0;">The Answer!</div><br />[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br />[ComVisible(true)]<br />public interface IFoo {<br />&nbsp;&nbsp;&nbsp;<span style="font-weight:bold;">[MarshalAs(UnmanagedType.AsAny)]</span><br />&nbsp;&nbsp;&nbsp;IBusinessObject[] GetBusinessObjects();<br />}<br /><br /></div><br /><br />What's weird is that this produces a compiler warning making it sound like the attribute is not of any use:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><span style="font-family: courier new,sans-serif;"><br />Type library exporter warning processing 'MyNamespace.IFoo.GetBusinessObjects(#0), MyProject'. Warning: Type contains [MarshalAs(AsAny)], which is only valid for PInvoke. <span style="font-weight:bold;">The MarshalAs directive was ignored.<br /></span><br /></div><br /><br />If you place the attribute on the actual implementation you do not get the compiler warning...but you also get the <span style="font-family: courier new,sans-serif;">Type Mismatch</span> again. So, it would seem the warning is ignorable as it is applying to something.<br /><br />Something tells me that once I get my hands on Adam Nathan's book, this will become a lot more obvious to me, and/or I'll find a better answer. Either way, I've got a solution for now.<br /><br /><br />So, why this post? Mostly as a note-to-self as to how to solve the problem in the future, but there's the hope that some poor sap such as myself has had the exact same problem and that this will turn up for them while they spelunk Google.<br /><br />If this proves useful to anyone, please, drop me a line.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com7tag:blogger.com,1999:blog-9236822.post-57849747241904961512007-06-28T08:27:00.000-04:002007-09-13T15:11:34.077-04:00Weird SQL2005 TempDB Table and Primary Key BehaviorOk, here's something I ran into the other day while working with some temp tables. I needed a temp table. That table was probably going to be bloated, and could benefit from a primary key and some extra indexes on it since I'd be doing some heavy queries against the table. The SQL used in this post isn't the same SQL as from my app, rather, it's been simplified to the point of demonstrating my problem.<br /><br />Consider the create-table statement below:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 36px; vertical-align: middle; background-color: #FFF7C0;">Figure 1</div><br /><span style="font-family: courier new,sans-serif;"><br />CREATE TABLE #myTmp (<br />&nbsp;&nbsp;&nbsp;[id] [int] NOT NULL,<br />&nbsp;&nbsp;&nbsp;[id2] [int] NOT NULL,<br />&nbsp;&nbsp;&nbsp;[foo] [decimal](6,3) NOT NULL,<br />&nbsp;&nbsp;&nbsp;CONSTRAINT [PK_myTmp] PRIMARY KEY CLUSTERED ([id] ASC, [id2] ASC))<br /></span><br /></div><br /><br />Ok, so this gives us a very simple temp table that's scoped to the connection (or stored procedure) that it's create inside. It also has a primary key.<br /><br />I'd been designing this temp table, and testing my SQL in a query window inside Management Studio. I had run this SQL, and the query window and it's connection were still open when I copy-n-pasted the code into my application and took it for a test run.<br /><br />I got an error:<br /><span style="font-weight:bold;">"There is already an object named 'PK_TmpWorking' in the database."</span><br /><br />Whaaaaaat? It's as though the name of the primary key doesn't follow the same scoping rules as other objects created in TempDB! This seems really weird to me, and my google-spelunking didn't turn up many answers (am I loosing my touch?) The only way around this was to not name the primary key constraint, like so:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 36px; vertical-align: middle; background-color: #FFF7C0;">Figure 2</div><br /><span style="font-family: courier new,sans-serif;"><br />CREATE TABLE #myTmp (<br />&nbsp;&nbsp;&nbsp;[id] [int] NOT NULL,<br />&nbsp;&nbsp;&nbsp;[id2] [int] NOT NULL,<br />&nbsp;&nbsp;&nbsp;[foo] [decimal](6,3) NOT NULL,<br />&nbsp;&nbsp;&nbsp;PRIMARY KEY CLUSTERED ([id] ASC, [id2] ASC))<br /></span><br /></div><br /><br />This leaves SQL Server to come up with a name for the primary key, and it appears that it names it some random jibber-jabber.<br /><br />So, while I can't really logic-out an answer as to why I'm seeing this behavior, I do at least have a work-around. But get this, as if this primary-key thing isn't weird enough, it would seem this 'bug' only exists for primary keys, and <b>not</b> other named indexes. What I haven't shown you is that, in my query window, I also had a number of CREATE INDEX statements, similar to:<br /><br /><div style="border: 2px solid rgb(0,0,0)"><br /><div style="border-top: solid 2px #FED626; border-bottom: solid 2px #FED626; padding: 4px 4px 4px 36px; vertical-align: middle; background-color: #FFF7C0;">Figure 3</div><br /><span style="font-family: courier new,sans-serif;"><br />CREATE NONCLUSTERED INDEX [IX_MyTmp_Foo] ON #myTmp ([foo] ASC)<br /></span><br /></div><br /><br />I had this executed in my query window. The temp table had it's randomly-named primary key, and it had this named index. When I ran the same code in my application, there was no complaints at all. I would have expected to run into the same "There is already an object named 'IX_MyTmp_Foo' in the database." error, but I didn't get one!<br /><br /><br />Does any of this make sense? Can anyone explain why it appears that primary keys on temp tables aren't scoped within the same scope as the temp table itself? Especially since regular indexes appear to be scoped as you would assume.<br /><br />Signed:<br /><br /> Confused in Yooperland.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com2tag:blogger.com,1999:blog-9236822.post-22741196160672071262007-06-21T14:00:00.001-04:002007-06-21T14:06:35.038-04:00Oh wowwwww...Ok, in this day in age, it's difficult to make me so awe-struck that my jaw hits the floor. Well, I finally got around to checking out the Tech Preview of Microsoft Live Labs' Photosynth, and let me tell you, my jaw is still on the floor. Totally awesome technology.<br /><br />I'm going to give you guys two links, one that will take you to a MS Live Labs blog post containing a video of a guy at TED (Technology, Entertainment, Design) Conference in Monterey, California explaining a couple of new MS-acquired technologies. Photosynth is included. Find that post <a href="http://labs.live.com/Photosynth+At+TED+Conference.aspx">here</a>.<br /><br />Then, go and install and <a href="http://labs.live.com/photosynth/">try Photosynth yourself</a>. You will need to install the browser plugin to use it.Jason Pollhttp://www.blogger.com/profile/13833122086713002530noreply@blogger.com3