Could we not word it like that?

July 29th, 2010

My wife drove me into work the other day, and noticing there were two people in the car, she slid into the high occupancy lane of the toll road.

As she did so, she commented, “You’re HOV-positive.”

Find and Replace in Word using C# .NET

July 3rd, 2010

Heads up, this article contains high quantity of geek content. Non-geeks should move along.

I’ve been trying to use Microsoft.Office.Interop.Word to perform a global bulk search and replace operations across an entire document. The problem was, however, if a document contained a floating text box, which manifested itself as a shape object of type textbox, the find and replace wouldn’t substitute the text for that region. Even using Word’s capability to record a macro and show the VBA code wasn’t helpful, as the source code in BASIC wasn’t performing the same operation as inside the Word environment.

What I wanted was a simple routine to replace text anywhere inside of a document. If you Google for this you’ll get the wrong kind of textbox, the wrong language, people telling you not to use floating textboxes, and all kinds of weird story iterators.

One site seemed to have the solution; many kind thanks to Doug Robbins, Greg Maxey, Peter Hewett, and Jonathan West for coming up with this solution and explaining it so well.

However, the solution was in Visual Basic for Applications, and I needed a C# solution for a .NET project. Here’s my port, which works with Office 2010 and Visual Studio 2010 C#/.NET 4.0. I’ve left a lot of redundant qualifiers and casting on to help people searching for this article.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Office.Interop.Word;

// BEGIN: Somewhere in your code
Application app = null;
Document doc = null;
try
{
  app = new Microsoft.Office.Interop.Word.Application();

  doc = app.Documents.Open(filename, Missing, Missing, Missing, Missing, Missing, Missing, Missing, Missing, Missing);

  FindReplaceAnywhere(app, find_text, replace_text);

  doc.SaveAs(outfilename, Missing, Missing, Missing, Missing, Missing, Missing, Missing, Missing, Missing);
}
finally
{
  try
  {
      if (doc != null) ((Microsoft.Office.Interop.Word._Document) doc).Close(true, Missing, Missing);
  }
  finally { }
  if (app != null) ((Microsoft.Office.Interop.Word._Application) app).Quit(true, Missing, Missing);
}
// END: Somewhere in your code             

// Helper
private static void searchAndReplaceInStory(Microsoft.Office.Interop.Word.Range rngStory, string strSearch, string strReplace)
{
    rngStory.Find.ClearFormatting();
    rngStory.Find.Replacement.ClearFormatting();
    rngStory.Find.Text = strSearch;
    rngStory.Find.Replacement.Text = strReplace;
    rngStory.Find.Wrap = WdFindWrap.wdFindContinue;

    object arg1 = Missing; // Find Pattern
    object arg2 = Missing; //MatchCase
    object arg3 = Missing; //MatchWholeWord
    object arg4 = Missing; //MatchWildcards
    object arg5 = Missing; //MatchSoundsLike
    object arg6 = Missing; //MatchAllWordForms
    object arg7 = Missing; //Forward
    object arg8 = Missing; //Wrap
    object arg9 = Missing; //Format
    object arg10 = Missing; //ReplaceWith
    object arg11 = WdReplace.wdReplaceAll; //Replace
    object arg12 = Missing; //MatchKashida
    object arg13 = Missing; //MatchDiacritics
    object arg14 = Missing; //MatchAlefHamza
    object arg15 = Missing; //MatchControl

    rngStory.Find.Execute(ref arg1, ref arg2, ref arg3, ref arg4, ref arg5, ref arg6, ref arg7, ref arg8, ref arg9, ref arg10, ref arg11, ref arg12, ref arg13, ref arg14, ref arg15);
}

// Main routine to find text and replace it,
//   var app = new Microsoft.Office.Interop.Word.Application();
public static void FindReplaceAnywhere(Microsoft.Office.Interop.Word.Application app, string findText, string replaceText)
{
    // http://forums.asp.net/p/1501791/3739871.aspx
    var doc = app.ActiveDocument;

    // Fix the skipped blank Header/Footer problem
    //    http://msdn.microsoft.com/en-us/library/aa211923(office.11).aspx
    Microsoft.Office.Interop.Word.WdStoryType lngJunk = doc.Sections[1].Headers[WdHeaderFooterIndex.wdHeaderFooterPrimary].Range.StoryType;

    // Iterate through all story types in the current document
    foreach (Microsoft.Office.Interop.Word.Range rngStory in doc.StoryRanges)
    {

        // Iterate through all linked stories
        var internalRangeStory = rngStory;

        do
        {
            searchAndReplaceInStory(internalRangeStory, findText, replaceText);

            try
            {
                //   6 , 7 , 8 , 9 , 10 , 11 -- http://msdn.microsoft.com/en-us/library/aa211923(office.11).aspx
                switch (internalRangeStory.StoryType)
                {
                    case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesHeaderStory: // 6
                    case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryHeaderStory:   // 7
                    case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesFooterStory: // 8
                    case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryFooterStory:   // 9
                    case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageHeaderStory: // 10
                    case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageFooterStory: // 11

                        if (internalRangeStory.ShapeRange.Count > 0)
                        {
                            foreach (Microsoft.Office.Interop.Word.Shape oShp in internalRangeStory.ShapeRange)
                            {
                                if (oShp.TextFrame.HasText != 0)
                                {
                                    searchAndReplaceInStory(oShp.TextFrame.TextRange, findText, replaceText);
                                }
                            }
                        }
                        break;

                    default:
                        break;
                }
            }
            catch
            {
                // On Error Resume Next
            }

            // ON ERROR GOTO 0 -- http://www.harding.edu/fmccown/vbnet_csharp_comparison.html

            // Get next linked story (if any)
            internalRangeStory = internalRangeStory.NextStoryRange;
        } while (internalRangeStory != null); // http://www.harding.edu/fmccown/vbnet_csharp_comparison.html
    }

}

Let me know if it worked for you; bug fixes and enhancements welcome.

Holiday Inn, Holiday Over

June 19th, 2010

As with most things, it’s the little details that matter, and for my most recent vacation, everything about staying at Holiday Inn got on my last nerve, so much so that we checked out a day early on a pre-paid room and were glad to do so. This blog is simply a note to myself, reminding me to avoid that chain when booking hotels in the future.

Overdraft and Bad WiFi

So I won’t forget the why: in my opinion, the first sign something wasn’t right was literally a sign indicating they would put a hold on accounts that may result in overdraft fees that wouldn’t then be their fault; nice welcome. There was no in-room refrigerator so we couldn’t store food or drinks over night. There were no tissues. The toilet always took three attempts to flush. The toilet paper was on the opposite wall of the toilet, better than an arm’s reach away (brilliant). The tub felt like the was grit on the bottom of it. The toilet was crammed between the sink and the tub, just enough to bang one’s elbows. Same with the soap dishes in the tub, at elbow height. It was a horrible room design, where just slightly too much space was squeezed out. For instance, if you sat in a chair, you hit your head on the lamp. There was no exhaust fan in the bathroom. The wireless was horribly slow and kept requiring an annoying re-authentication process at random. Our room keys spontaneously and simultaneously stopped working, and when we went to the front desk to get them fixes, we were blamed for having them near a cell phone, that we know for a fact that wasn’t the case. I could go on, but I just wanted enough keywords so I could later find that place I didn’t ever want to stay at again.

Admittedly, some of this could have been the room, that hotel, its staff, or that chain. I feel little inclination to explore the matter further, I’m annoyed that much that I’m just done. I don’t expect perfection, but I also don’t want to loath returning to have to deal with the next surprise; certainly not on vacation.

But further related insult, though now not too surprising in retrospect, came when we were walking around Broadway at the Beach and noticed a number of signs at ticket areas that said Medieval Times Dinner & Tournament‎, 2 adults for $20. It was a deal that sounded too good to be true, and was. There was no branding or other information on the sign, but when one went to purchase the tickets, you couldn’t; what you got was a rep whipping out an appointment calendar for a timeshare tour. If you would go take their off-site multi-hour tour and listen to a sales pitch, they’d “give” you the tickets for “free.” But if you wanted to buy what they advertised, they’d never sell them to you. It was very bait and switch. Turns out, it was… Holiday Inn. When asked how come it didn’t say Holiday Inn or indicate there was a sales pitch, we were flat out told that people wouldn’t come in if they had put up the truth. This just solidified my vision of the corporate image.

Your own experiences may vary, but having stayed at other places in the same price range, I’m avoiding this chain.

Walt gives Holiday Inn one thumb down.

Help Wanted, Carpenter

June 17th, 2010

My wife was driving down a highway in Pennsylvania and looking out the window I saw a scene before me that made me say out loud, “Oh, that’s just not right.” I instructed my wife to make the next available U-Turn and pull onto the shoulder. I needed a photo.

Makes me wonder what happened to the last one they had.

There before us was a sign that said ‘Help Wanted’ ‘Lead Carpenter’. And, behind it, a set of three crosses, one that belonged to a carpenter. Either way you look at it, that something happened to their last one, or that the need make more crosses, that’s most likely not the best location for this particular help wanted sign.

Pages: 16 Exabytes

May 10th, 2010

So I decided to make some business cards using Pages, however after adding a few tiny JPGs, I started getting the spinning beach ball — you know the one, the one you’re never supposed to get in OS X.

So, I decided to open Activity Monitor to see what was up.

Pages had allocated 16 Exabytes of memory. My computer has 6 Gig.

Remember, it goes from Megabytes, to Gigabytes (1,000Meg), to Terabytes (1,000Gig), to Petabytes (1,000 TB), to Exabytes (1,000 PB).

16 Exabytes

Netflix Recommends

May 10th, 2010

Apparently because I like John Clease’s comedic timing, cursing magicians, and canceled 80s comedies, that means I like Carl Sagan’s COSMOS.

Netflix Recommends

Here’s the sad part: they’re right.

An expensive TV recommendation

April 2nd, 2010

So, I’m at work and we’re discussing addictive television programs.

My co-worker turns to me and says, “Have you seen Breaking Bad?”

I’m like, “No. Is it any good?”

He says, “It sure is!” And then he goes on to tell me how it’s on AMC, and that’s it’s about a chemistry teacher that turns into a drug kingpin. The how and why are part of the enticing drama.

So, I pull out my little scrap paper and write ‘breaking bad‘ in my notes to remind myself to TiVo it.

Normally the story would end there, but my wife happened to empty out my pockets, saw the note “breaking bad,” assumed my spelling was atrocious, and concluded she needed to take my car in to have repairs done.

$230 dollars later I’ve got shiny new brakes. “Funny thing is,” I’m told, “the shop didn’t think they needed replacing.”

Virtual Server Problems

April 2nd, 2010

I recently switched operating system vendors, and to my surprise when I went to port over the web content from one system to another, things didn’t go as smoothly as I had hoped. What used to be /etc/httpd was now /etc/apache2, and inside this directory files were organized differently that I was used to, and so forth. Still, I would have hoped moving from Apache2 on RedHat to Apache2 on Ubuntu would have been easier.

Empirical evidence was suggesting that all of my user’s virtual sites were working, but all of mine, no matter where I had them on the system, were reporting the error: You don’t have permission to access / on this server.

Here’s what was going on, primarily recounted so if I ever do this to myself in the future, I’ll know what to look for.

The virtual host files, which now appear in /etc/apache2/sites-available as *.conf files, had a slight difference. Some of them had this in their Directory directive:

    Order allow,deny
    allow from all

Mine did not. But, then again, some other websites did not as well. Turns out those directives were placed inside their .htaccess files.

Now, not all sites had the .htaccess file, and things had been working before without the explicit directive in each virtual host .conf file.

Turns out I had some how tromped on the default file, which contains a directive that looks like this:

    <Directory />
      Options FollowSymLinks
      AllowOverride None

    </Directory>

If it is not present, then all virtual hosts must explicitly allow access (via .htaccess or their .conf file).

This directive allows Apache to serve up any file the URL asks for… which one may not want to do. It seems the secure way is to edit the virtual host .conf files, and not rely on some default magic.

But, because that was in my old configuration long ago, and not in the new one, my virtual host .conf files didn’t have it, but my more modern ones for my users did. Depending on which template I used to base new sites off was how some sites worked and some didn’t.

After fixing this, I ran into a new problem. Some sites weren’t coming up still, but this time with permission errors.

When I migrated over the web content, it preserved user and group ownerships from the other system. These did not match the new Apache2 user and group on the new system.

However, I got lucky. There was no user/group mapping on the new system, which meant I could execute a find command to find and fix them. It looked something like this:

    find /home -nogroup -print
    find /home -nouser -print
    find /home -group 49 -exec chgrp www-data ‘{}’ \;

A Level Perspective

February 14th, 2010

There are a lot of applications available for the iPhone, and thanks to the magic that makes orientation detection possible, some clever person produced an application that visually shows a level, bubble and all.

iPhone Level

Today I was witness to a young child asking to borrow an iPhone, and with much curiosity he opened this particular application.

Unfamiliar with a level, it’s function, or operation, he innocently asked, “What one was supposed to do?”

“You’re supposed to put the bubble between the two marks,” came the adult explanation.

The kid tilted the iPhone back and forth, watching the bubble slide to and fro, and leveled it holding the bubble between the two lines for a moment.

At that moment he blurted out excitedly, “Look, I’m winning!”

Unfortunately, his excitement caused the bubble to shoot to one end, just as I had time to click a picture of the event.

WebCams on OS X

September 23rd, 2009

OS X is capable of using USB and Firewire cameras, with perhaps the most famous being the iSight, second to the Built-in iSight of Apple’s laptops.

Use other cameras!
But it turns out you can use a lot of third party cameras using the Macam driver.

Use multiple cameras!
If you’re using multiple cameras at once, say for security monitoring, you’re going to want to take a look at SecuritySpy, which has motion detection, time lapse, and the ability to view from a remote browser, plus many other features.

Barcode Reader!
EvoBarcode will let you use your camera as a bar code reader.

Web Streaming!
EvoCam will let you stream multiple cameras and push images to servers.

Stop Motion Photographer!
iStopMotion will help you make your own stop motion movies.

Real-time Special Effects!
iGlasses will enhance and alter your video feeds.


Bad Behavior has blocked 391 access attempts in the last 7 days.