Andornot Consulting Inc.
Home Page
Home Page
 |  | 

Thursday, May 31, 2007

Watch out for file encoding when importing into Inmagic Textworks

When importing a text file into a Textworks database, make sure it is ANSI/ASCII encoded, not UTF-8 or otherwise.

You can't tell just by looking at the text how the file has been encoded, but a good text editor will tell you, and allow you to save as another encoding format.

I got the following error message when attempting to import a UTF-8 encoded comma-delimited text file into a catalog textbase.

Note that Textworks is not going to warn you about encoding incompatibilities specifically. It's just going to drastically misinterpret characters. Lucky for me this happened before import got underway, as the misinterpretation involved a field name match.

As a nota bene, the above shows how misleading it is to refer to any delimited text file as an "ASCII delimited file," as I've heard them referred to before. Unless of course you happen to mean literally ASCII-encoded as opposed to UTF.

Tuesday, May 22, 2007

Virtual PC 2007 and NVIDIA nForce networking conflict on Vista

I have Vista 32-bit on an Intel x86 NVIDIA nForce4 motherboard. Ever since I installed Vista, networking has been... dicey. Any time the computer went to sleep/hibernate, networking would no longer work. Some BSODs on wake. Installing the KB931671 Windows Update reduced the number of BSODs, but not the problem itself.

Updating to the latest nForce4 drivers (15.00, released Feb 5 2007) caused networking to simply stop working from startup. No ethernet, no internet, no connection to other workgroup computers, no ping to gateway even (transmit failed, error code 1231).

Through a process of trial and error, I found that disabling Virtual Machine Networking Services on the nForce Networking Controller, plus netsh interface ipv6 reset from the command line, restored networking. (Virtual Machine Networking Services are installed by Virtual PC 2007 as part of its Virtual Machine Additions.)

So I'm left with no network connectivity on my virtual machines. Perhaps I should count myself lucky I have connectivity on the host, but instead I think, no, a steaming pile of curse words followed by a new NIC will be the answer.

UPDATE June 8, 2007 - Bought a new network card and disabled nVidia networking. All problems resolved. Cost? $35.

Labels: ,

Thursday, May 17, 2007

How to serialize an object to Inmagic Textworks userStore with JSON

The Inmagic DB/Textworks and CS/Textworks script object model includes a Store object which you can use to store strings. It's a simple dictionary object of key/value pairs where the key is a unique identifier for the corresponding value. There are two versions of the Store object: userStore and sessionStore. The first persists key/value pairs to the local machine, while sessionStore only sticks around so long as Textworks is open.

It's a lot like writing cookies. If you need to keep some values around to reuse that would otherwise disappear when the form unloads, you need to use the Store object.

set:

var myString = "foo";

Application.userStore.value("keyName") = myString;

 

get:

var myString = "";

myString = Application.userStore.value("keyName");

Unfortunately it only stores a string, like a cookie. If you like to work with objects, it doesn't seem right to store individual object properties as separate key/value pairs. Especially if you decide to change the object profile down the road, because you will have to rewrite code. It would be great to simply store an object's state and pick it back up again whenever.

Serialize the object

Serialization is the act of saving an object into a binary or text format. Aha! Text format - that's a string. We can save our object to a string, and persist it to the Store object. Then later we can get that string and deserialize back into an object.

What's the best way to store the string so we can easily turn it back into an object? We could roll our own format, but it's best to stick with something already known and tested and out there. So let's use JSON. JSON stands for JavaScript Object Notation and it's a text-based human-readable format for representing data structures, commonly for transmission over an HTTP connection. Basically it's AJAX without the X (Object Notation instead of XML).

JSON Example

In the following example, we define a Person object, instantiate it, set its properties, serialize it, store it, retrieve it, and deserialize back into an object.

function Person()

{

    this.Name;

    this.BirthDate;

}

 

// instantiate a Person

var person = new Person();

person.Name = "Joe Blow";

person.BirthDate = new Date("July 1, 1975 08:30:00");

 

// serialize the Person object

var strPerson = person.toJSONString();

/*

strPerson =

{"name":"Joe Blow","BirthDate":"1975-07-01T08:30:00"}

*/

 

// store the serialized Person

Application.userStore.value("mykey") = strPerson;

 

// retrieve the serialized Person

var s = Application.userStore.value("mykey");

 

// deserialize from string back into object

var o = s.parseJSON();

Pretty simple!

JSON Source Code

The prototype functions toJSONString() and parseJSON() are provided by an open source JSON parser and stringifier at http://www.json.org/json.js which can be compressed to less than 2000 characters and placed in the form script.

toJSONString() serializes strings, dates, arrays, booleans, numbers, nested versions of these... pretty much anything. parseJSON() is a more secure rewrite of javascript eval which limits itself to valid JSON notation only.

A Short Aside About Eval

If you're not already aware of how powerful eval is, consider that you can pass in a string and have it interpreted as code. You can say:

eval("var a = 1");

and thereafter have a variable, a, which equals 1. This is incredibly handy, but also a security concern, so you would never eval anything you didn't have complete trust in. You can directly eval a JSON string and magically get your object back, but parseJSON() is safer.

Wednesday, May 16, 2007

How to make a javascript trace log

After I wrote my previous post on handling script errors, it occurred to me I had taken knowledge about about tracing and exceptions for granted. So, here is a description of how I add a trace log and exception handling to my scripts in DB/ or CS/Textworks.

Make an Inmagic DB/Textworks Trace Log

First, add a form box and two buttons to the form, named boxDebugLog, btnBugs, and btnClearLog.

Add code to handle the two buttons and and a log method, which writes a new line into the form box whenever called. The bugs button ensures there is no syntax error lurking in the code, which would halt any code from running on the form, and therefore would stop "no syntax bugs" from being written into the log (no news is bad news). The isLogging boolean variable can be turned on and off globally so that trace statements don't have to be ripped out of deployed code.

var isLogging = true;
function btnBugs_onClick()
{
  log("no syntax bugs");
}
 
function btnClearLog_onClick()
{
  Form.boxes("boxDebugLog").content = ''
}
 
function log(val)
{
  if (!isLogging)
  {
    return
  }
 
  var box = Form.boxes("boxDebugLog");
  if (box.content != '')
  {
    box.content += "\n"
  }  
  box.content += val;
}

Within the code, insert trace statements wherever you want to see the results of an operation.

log("GetRequestorsByQuery() " + commandQuery);

The result of the above would look like this in the Debug Log form box:

Extend the Trace Log to Handle Exceptions

The above is good for viewing the results of expected steps in a series of operations and can help you debug. Of course, when you work with code the real bugbear is the unexpected. And, further, there are operational errors (not bugs) which may crop up long after you're done building the code.

Inmagic DB/Textworks presents us with an obstacle in that any error encountered simply halts the process and does not in any way indicate there has been an error. To the average user this is perhaps a blessing (ignorance being bliss), but to the developer it is nothing but an impediment. Fortunately there is a way to free the errors from their airless dungeon so as to inform you of what is really going on.

The try/catch/finally statement attempts operations with instructions (the catch block) on what to do if an exception is encountered in the try block. The finally block tells what to do after try and catch have been dealt with, and is useful for closing objects or releasing resources that might otherwise have been stuck as a result of the exception encountered.

In our case, we can use try/catch to handle exceptions, and log the error messages to the trace log.

  var item = new catalogItem();

  var isInserted = false;

 

  try

  {

    isInserted = InsertCatalogRecord(item); 

  }

  catch (err)

  {

 

    log("Unable to add to catalog. " + err.name + ": " + err.message);

  }

Note that the err variable is an Error object, and so has predictable properties to call upon, name being the exception type and message being the short description. (More about exception types.Error is a built-in javascript object. I should mention that it is normally good practice to isolate specific exception types with multiple catch blocks, but I think that's overkill for Inmagic scripting.

You can also generate custom exceptions using the throw statement.

throw new Error("something went wrong");

throw "something went wrong";

Both statements are valid, because javascript is extremely lenient about what you can throw. I prefer to throw an Error object instead of a string, to be consistent with other exception types that might come up. Then I can ask for err.name and err.message without having to first check and see if the exception is actually a string instead.

So the throw statement allows you to "throw" (i.e. raise) an error whenever you like. Why would you want to, if the whole point of this exercise is to eliminate errors from the script? The reason becomes clear when you are working with operations whose scope is beyond your immediate control. In the scenario above where try/catch is demonstrated, we are adding a new record to a catalog. It's possible the catalog might not be there, or might reject a new record for some reason, or a field name changed since we wrote the script. If any of these happens, or anything else we can't now predict, we can still catch the problem and deal with it in a predictable way so that the script fails gracefully and passes on vital information before it expires.

If you wanted to force the Inmagic recordset object to throw any error it encountered, you could do that, for instance (and I do).

      if (rs.Errors.Count > 0)

      {

        throw new Error(rs.Errors(0).Description);

      }

It takes a bit of an adjustment at first to deliberately throw an error, or to let an error bubble up to another, calling function and deal with it there, but it is a powerful and freeing concept once you get it.

Tuesday, May 15, 2007

Handling Inmagic Textworks javascript errors

The scripting environment within Inmagic DB/Textworks (or CS/Textworks) is extremely primitive. Controls you want to interact with have to be declared in an environment separate from the form designer and it's hard to write code longer than 100 lines because there's no tab character or ctrl-a select all, let alone syntax highlighting or intellisense. It's very easy to lose your way if your script does anything beyond "hello world."

The most difficult thing to deal with is that any javascript error encountered does not actually throw an error. Things just progress... until they don't. No error thrown, no feedback, no nothing. It makes debugging a nightmare. What I have done until recently is insert a million-and-one trace statements that write to a trace box over on the side of the form, which at least tells me how far progress went and gives me a clue as to the general area things went wrong.

But I've recently realized I *can* throw (and handle) an error, if I wrap logical blocks of code in try/catch/finally statements. Can't believe it took me so long to cotton on to this. Now I can get detailed info about unanticipated exceptions as well as the anticipated ones. It makes my life better until I can boot up Visual Studio and bask once again in its warm developer-friendly glow.

Play "spot da error":

Yeah, "rs" is an undeclared variable. That's what happens when you refactor manually. Without try/catch this little problem becomes extremely elusive.

UPDATE: The picture has syntax highlighting because I do all scripting in UltraEdit. (Also features code folding, a jump-to-function list, etc.)

Wednesday, May 09, 2007

MIX 07 session videos for all

I didn't go to MIX 07, Microsoft's conference for Web designers, developers and decision makers, but I am shamelessly sucking down the conference session videos. I've watched about 3 hours so far, and already had some great eye-openers, particularly about LINQ and Visual Studio "Orcas". (Not Silverlight so much yet.)

Here's the feed to the session videos - well worth a look! There's bound to be at least something to get you excited: http://sessions.visitmix.com/rss/mix07_rss.xml.

Here's Anders Hejlsberg talking about LINQ, the one I liked so much: how LINQ (Language INtegrated Query) unifies data operations on disparate data formats (relational tables, XML, in-memory objects).

Tuesday, May 08, 2007

Re-partitioning a dual boot Windows Vista and XP system

If you're into adventure on the high seas, extreme blood sports, or are simply a masochist looking for your next hit of sweet, sweet pain, look no further. Have I got an activity for you.

Ladies and gentlemen, I direct your attention to this hard disk. On the C:\ partition, I give you: Windows XP. On the V:\ partition: Windows Vista.

"Hm. V:\ too small, and C:\ very big. I will shrink C:\ and allocate extra space to V:\," says I. Jauntily. I should have paid attention to the flash of green lightning and the ominous thunder-rumble that occurred when I said that. But I did not, and I stand before you now a sadder, but infinitely wiser man.

Here's what I did, minus the cursing and backtracking, minus the time spent on Google and various unhelpful Microsoft KB articles.

  1. Back up everything.
  2. Get GParted (Gnome Partition Editor) LiveCD, an open source partition manager. Burn to disc. Boot to said disc.
  3. Shrink C:\. Extend V:\. Mix, stir, let stand 3 hours.
  4. Reboot to Windows Vista install disc. Select "Repair this computer". (It repairs. Quickly.)
  5. Reboot, this time to boot manager and to installed version of Vista on V:\. Chkdsk isn't at all sure what the hee-haw you've just been doing and wants to check the disk. Let 'er rip.
  6. Log in to Vista. Ta-da. But what about XP? Yeah, it's hooped. (When you try to boot to XP, it says "\ntldr is missing." XP System Recovery Console can't find a Windows XP install, can't map the C:\ partition, can't find its own buttocks with two hands and a flashlight.)
  7. While in Vista, download VistaBootPro. It's free. Install. Run.
  8. From Manage BCD OS Entries, select Windows XP, or rather, "Earlier Version of Windows". Rename that entry to "Windows XP" while you're at it. Select "Change Boot Drive." Select C: from the dropdown. Apply Updates.
  9. Reboot to boot manager and to installed version of XP on C:\. Success!

The above has been a highly condensed version of my experience today. It worked on my machine. Your mileage may vary.

Labels: , ,

Friday, May 04, 2007

Use Notepad2 in Vista

To use Notepad2 as your default notepad application, follow the instructions found at http://www.mattberther.com/?p=828. Much easier than making things work in XP.
  1. Download and extract Notepad2.
  2. Rename Notepad2.exe to notepad.exe.
  3. Find c:\windows\notepad.exe and c:\windows\system32\notepad.exe and set the owner to ‘Administrators’, and grant Administrators full control.
  4. Using Windows Explorer, drag and drop the renamed notepad2.exe to c:\windows and c:\windows\system32.

Labels: ,

Wednesday, May 02, 2007

IIS and Skype: Web site won't start

Oh mercy me. What to do when you attempt to start a web site in IIS and you're confronted with something along the lines of "There was an error while performing this operation. The process cannot access the file"? Of course, after wasting 20 minutes googling (thinking that it could be any number of things I've got going on my machine, chiefly that I'm running Vista) and discovering kb articles like http://support.microsoft.com/kb/890015 I figured out that yes, something is using port 80, but what? Along comes http://forums.iis.net/thread/1636766.aspx to the rescue: fire up Windows Task Manager and see what's running under the listed process ID, but first, ARGH! Only now do I remember that this same thing happened over a year ago on my XP machine: good ol' Skype had snagged port 80 before IIS could get to it leading to the same useless error message. The pain. Sometimes Skype gets to port 80 first, and sometimes IIS. So sometimes all is well, and other times...not. Make sure that all is well all the time by doing the following: Open up Skype -> Tools -> Options -> Connection and uncheck "Use port 80 and 443 as alternatives for incoming connections." Of course, you might need to have this checked due to a corporate firewall or something, but.... For those wondering, to find out whether port 80 really is in use, open a command window and type in "netstat -ano" without the quotes. If it's listed it's being used. Wouldn't it be nice if the error message said "port 80 is in use so this web site cannot be started"?