Archive for the 'Hack' Category

Find and Replace in Word using C# .NET

Saturday, 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.

Printing in Parallels

Wednesday, August 12th, 2009

Parallels is a virtualization package for the Macintosh that primarily is used for running Microsoft Windows in a virtualized environment on OS X.

At some point you’re going to run into the problem of wanting to print something from the guest OS. Do not try to install a Windows XP print driver for the device that’s connected directly to your Apple. That’s not how it works.

You have a virtual machine. Surprise, you have a virtual printer too.

To set it up is trivial:

  1. Stop your Windows VM if it’s running.
  2. Open VM Configuration Editor (Parallels Desktop menu – Edit – Virtual Machine)
  3. Add Parallel Port Printer to the VM Configuration: click “Add” – select “Parallel Port”, hit “Next” – select “Use a printer” – select the printer you have available in the Mac OS.
  4. Make sure that you are able to print using that printer from the Mac OS side.
  5. Start Windows and try printing some document using “HP Color LaserJet 8500 PS” (it’s generic driver that’s being used for printing from the Virtual Machine to any Mac OS compatible printer).
—Source: Parallels Knowledge Base #5036

This creates a HP Color LaserJet 8500 PS printer, which then gets redirected to the host operating system’s default printer. Printing then works normally, queuing and all.

Now, I did run into this problem using Microsoft Office on Windows XP with a HP DeskJet 6980 connected wirelessly through an Apple AirPort Extreme in bridge mode:

pstopdffilter/pstocupsraster failed with err number -31000

Here’s how I solved it.
I deleted the print queue on the host operating system, then I turned the power off and back on again on the printer, and tried again.

Seriously. I power cycled the printer. That’s all that was required. Second time through, it worked like a champ.

Big scary error message, itty-bitty solution.

NOTE: You will want to scan through your document if you’re using exotic fonts. In my case apostrophes were coming out as í.

Safari Problems Downloading .DMG Files

Monday, August 3rd, 2009

A while back I started having problems with Safari 4 being able to download files. Normally when one clicks on a .dmg or .zip file, Safari downloads it.

Recently, it stopped working, either doing absolutely nothing or trying to load the file into the browser itself for display. It was as if the MIME type wasn’t properly being handled.

Here’s how I fixed it.

It appears that Speed Download’s broswer plugin is to blame. While it works amazingly well with Safari 3, it doesn’t seem to work quite right with Safari 4.0.3.

  1. Quit completely out of Safari.
  2. Go to /Library/Internet Plug-Ins directory and locate the file SpeedDownload Browser Plugin.plugin and move it out of that folder.
  3. Restart Safari.

FIX: undefined symbol: apr_ldap_ssl_init

Monday, April 13th, 2009

This is a geek entry for resolving the problem:

* Restarting web server apache2
/usr/sbin/apache2: symbol lookup error: /usr/sbin/apache2: undefined symbol: apr_ldap_ssl_init [fail]

Non-geeks will want to move along…
(more…)

OS X Mail’s Strange Log Messages

Thursday, January 1st, 2009

A while back I installed a pretty neat Mail extension called MailTags, which was used to tag mail messages with additional information. Cool concept.

However, at the time the usage I was personally getting out of it didn’t warrant the price for the app, and I uninstalled the application after the trial period was over.

Unfortunately, things didn’t end there, because I kept getting repeated log messages like this when I looked at the console:

1/1/09 2:52:05 PM Mail[362] Cannot restore width of table column with identifier 24

It was really obvious (and annoying), as I use GeekTools to monitor my console on my desktop in order to keep a bird’s eye view on what’s happening in the background.

I found out that I was not the only other user having this problem, and the MailTags site had a solution invoked from the Terminal:

$ defaults delete com.apple.mail TableColumns

I’d done this before, but the problem resurfaced. Not sure why. And, doing it again seems to have fixed the problem, again. My log is back to normal.

Meanwhile, I discovered that that MailTags has a new version out, and perhaps I’ll give them a second chance.

I just tend to get worried when an extension appears to go deep, especially when we know Apple is about to revamp things with the next release of the OS, and cruft somehow got left behind before.

Invisible Drive on OS X

Saturday, December 27th, 2008

I happened to sign on to my desktop Mac and noticed something very strange, the Harddisk icon was no longer on the desktop.

Other clever tricks for looking at the file systems showed the file was most certainly present, although Finder operations were treating the volume as if the hard disk was hidden or invisible. The drive was there when I used terminal and did $ ls -l /Volumes

Finder Preferences showed that icons should be shown, but just the drive icon wasn’t appearing.

Then I found this aritcle, which suggested firing up the Script Editor and running this script:

tell application “System Events”
set visible of disk “NameofDisk” to true
end tell
tell application “Finder” to quit
delay 1
tell application “Finder” to launch

I believe I got myself into trouble by accident when I did the last disk repair using Disk Warrior or Disk Utility. Somehow the operation marked the drive as invisible. Undoing it was as simple as asking the system to make it visible again.

Garmin WebUpdater

Sunday, November 2nd, 2008

I own a Garmin GPSmap 60CSx in order to geoencode my photography using HoudahGeo.

Garmin now has a means up updating the firmware in their GPSs by using a WebUpdater, of which I use the version for the Mac.

I Got Myself Into Trouble
In retrospect, I got myself into trouble by starting the program, it failed to detect the GPS, to which I turn on the GPS, and plugged it into the USB port. While the WebUpdater saw the device and went to update, it stayed in the “Erasing… Do Not Unplug” state for about two hours before I got brave.

What I Did, And Boy Was I Lucky
I couldn’t cancel. I couldn’t Quit. So I had to Force Quite by using Command-Option-Escape, that at least got WebUpdater to stop. The GPS was still stating “Loader Loading…” when I pulled the USB, and when that didn’t change anything, I turned off the power to it. I wasn’t so sure I was going to see much of anything when I powered it back on.

I got lucky. I turn the power back on and I was still at the old revision. Then plugged in the USB to the computer. Then started WebUpdater, which again noticed the GPS version, downloaded the firmware again, and had no problems installing it. Seems doing things in this order works just fine.

My Plans If I Was Unlucky
Over on Bill Turner’s site, he’s written an article about Fixing a Dead Garmin GPSMap 60CSx. It seems he’s learned holding down the Power Button and the Up Arrow at the same time while starting the WebUpdater software (I think he has three hands to pull this off), he’s able to force the GPS to identify itself to the updater. Problem is, according to his instructions, you have to keep holding down these button chord during the update; some comments on the blog state it isn’t necessary, and there’ve been mixed results as to whether this works universally or not.

I’m not sure I would have had the bravery to just go killing processes plain outright, but since Bill did such a nice job of providing an alternative, I felt it was worth the risk — even if I didn’t have to go that route. Thanks Bill for blogging your GPS recovery notes.

Clever Bulk Rename Trick in Windows

Tuesday, October 7th, 2008

Ever want to rename a bunch of files to the same prefix, but have an incremental count after them?

From Explorer, select all the files that you want to bulk rename, right click, and select Rename.

While all of the files will be selected, only one is editable. Give the file a name, let’s pretend for the sake of discussion you typed ABC.jpg.

All of the rest of the files will be renamed ABC (1).jpg through ABC (n).jpg, where ‘n’ is the number of files minus one, since the first one doesn’t get a number.

Knowing this, you can do some clever stuff. Create one bogus file renamed to ZZZZZZZZ.TXT at the end of your list; select all the files, and bulk rename them as shown above. Then delete the bogus file, it should be the only one without a number, and you’ve just made a sequence of files.

OS X: Batch Rename from GUI

Sunday, April 9th, 2006

I keep forgetting about this trick, so I thought I’d post it in the event I have to ever do it again.

Part of the problem with a graphical GUI is that it’s very difficult to rename files in batches, for instance, prepending some text to a group of files.  This kind of thing is fairly trivial at the command line.

Apple has a facility to do this, but as it’s not something a regular user does often, it’s not enabled by default.  Here’s how to get all kinds of additional functionality out of OS X.

1) Open /Applications/AppleScript

2) Turn on Show Script Menu in menu bar

3) Optional: turn on GUI scripting, show library scripts, and choose where to show them.

You’ll notice up near the time in the menu bar a black scroll has appeared.

All the batch renaming and filename twiddling stuff is under the Finder Scripts.

UPDATE 19-Dec-2009: Upgrading to Snow Leopards deletes some useful scripts, specifically the Finder Scripts.

UPDATE 31-Aug-2010: The scripts live in “/Library/Scripts/Finger Scripts” and are

  • Add to File Names.scpt
    md5 4b0cd899acb19b5fc62ef2049d81a933 – 18114 bytes
  • Change Case of Item Names.scpt
    md5 af7429228be4d0e1a096092af5341c52 – 17808 bytes
  • Replace Text in Item Names.scpt
    md5 716493cab1c569953a7f40d76ed9a1f7 – 24328 bytes

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