VBScript/Classic ASP Function Parameters

I know that VBScript passes parameters by reference, but I seldom take advantage of that feature. So when I need it, I am always able to convince myself that I’m misremembering. So here are the results of a recent test to remind me:

function Three(N)
	N = 3
	Three = 0
end function

dim x
x = 1
Response.Write "<p>x = " & x & "; Three(x) = " & Three(x) & "; x = " & x & "</p>" & vbCrLf

Output:

x = 1; Three(x) = 0; x = 3

Why I Don’t Care About Swift

Swift is a new programming language created by Apple for use on OS X and iOS devices. The programming world is agog. Apple’s fantastic new language apparently solves all their problems, as evidenced, they say, by the fact that some programmer ported Flappy Birds to it in a few hours.

I’ve been around long enough to see languages come and go. Each claimed to solve all the problems introduced by its predecessors, yet each was replaced by a language that solved all its problems. In some cases, the new language surpassed the success of the language it replaced (C++ and Java); in other cases, the new language faded into obscurity (Modula-2 and Ada).

Lately the motivations for new languages have been dubious. There is a big emphasis on making a language easy to learn and having it hide nasty issues related to memory management and type safety. One review of Swift I read stated, “Apple hopes to make the language more approachable, and hence encourage a new group of self-taught programmers”. While that sounds great, it means that those of us who have mastered our craft after 30 years or more of practice are saddled with the training wheels and water wings that are written into these languages for the noobs.

A classic example is the lack of unsigned integers in Java. The motivation for this was to simplify the language for “new and self-taught programmers” by avoiding errors caused by a lack of understanding of sign-extension. However, for those of us who showed up for class the day that sign extension was taught (that would be day two), we’re left with a language that unnecessarily limits the range of positive integers and requires us to actually have mastered sign extension in order to understand what is happening when we directly manipulate the bits in our integer variables.

Explicit vs. Implicit Typing

One of the simplifications Swift makes is that it infers the types of variables from the values assigned to them rather than requiring the programmer to explicitly type variables. If this was true “weak typing” like I’m familiar with in VBScript, it would be great (though it would come with its own set of problems). But all Swift does is infer the type of the variable from the first value you assign to it.

This actually introduces problems, because it’s not always possible to unequivocally determine the type of a literal value. So Swift gives you ways to force it to interpret a literal value as a given type. Rather than removing the necessity of the programmer understanding types, Swift thus requires “new and self-taught” programmers to have a mastery of types so that they can understand how Swift is working behind the scenes and make sure that their variables have the desired type.

Strings

Swift is said to improve string-handling over Objective-C (the current language used on OS X and iOS). There is certainly room for improvement there. When I first started programming in Objective-C, one of the first things I did was bring over my own C++ string class, as I found NSString to be overly complicated and muddled. Over the years I’ve gotten better with NSString.

I would argue, however, that some of the so-called “improvements” in Swift with respect to strings are differences without a distinction. So instead of this in Objective-C:

[NSString stringWithFormat:@"The value of num is %d", num]

you say this in Swift:

"The value of num is \(num)"

The Swift version is obviously more concise, but it is also less powerful. To add more complex format specifications to Swift you actually have to invoke the functionality of the underlying NSString class, which means the “new and self-taught” programmer, again, needs to understand the details of the implementation in order to do anything beyond the simplest strings.

One of the stated benefits of string handling in Swift is that “all strings are mutable”. One need not worry about whether the string is declared as an NSString (immutable) or NSMutableString (mutable). Well, you don’t have to worry unless you do have to worry — strings assigned to constants are immutable in Swift. So:

var myString1 = "Mutable string"
let myString2 = "Immutable string"
myString1 += myString2    // perfectly legal
myString2 += myString1    // compile-time error

Switch Statements

Swift eliminates the “fall-through” behavior of switch statements, which is said to eliminate bugs caused by omitting the break at the end of each case block. But, oops, sometimes the fall-through behavior is exactly what you want. So Swift adds the fallthrough keyword. It could be argued that Swift eliminates a line of code (the break) while giving the behavior one normally desires. But at the same time, it adds a keyword (fallthrough) that does the opposite. This requires “new and self-taught” programmers to have the same thorough understanding of switch behavior that Objective-C and C++ programmers do.

Single-line Blocks

The Swift compiler will warn you if you omit the braces in any block (such as after an if) and does not allow single-line blocks, thus avoiding this error:

if (x < 0)
    goto fail;
    goto fail;

The code above will always execute one or the other of the goto statements in Objective-C or C++. Even though the second goto is indented, it is not part of the if-block and will be executed if the condition is false.

Swift will warn you about the missing braces and force you to write this:

if (x < 0)
    {
    goto fail;
    }
goto fail;

Or, for those of you who don’t do your braces the right way, this…

if (x < 0) {
   goto fail;
}
goto fail;

This is fine, and hard to argue with. The supposition is that the programmer will immediately recognize the flaw or won’t make the mistake in the first place. On the other hand, I would argue that the same C++ programmer who wrote the erroneous code will write this in Swift:

if (x < 0) {
    goto fail; }
    goto fail;

I always put braces around my blocks, even if they are one-line, so this doesn’t affect me. It’s ironic, however, that while Swift prides itself in eliminating the unnecessary break statement at the end of a case block, it requires two to four additional lines (braces) in if, for, and while statements, which are more numerous.

PocketBible and Swift

I will be more than happy to learn and use Swift for programming on iOS and OS X. I just don’t believe the hype and won’t convert just for the sake of doing something new.

I am a strong proponent of platform-independent languages like C, C++, Java, and, to a lesser extent, Objective-C (the latter is primarily an Apple language, though it has its origins outside of Apple). Such languages allow me to develop code on one platform and re-use it on another. One of the promises of C++ and Java was that you could develop the code for one platform and use it on many others. Swift is an Apple language (the same way C# is a Microsoft language). It only works on Apple devices. While those are numerous, they’re not the only devices out there. So rather than moving toward the “write once, read everywhere” model promised by Java, we’re back to “write everywhere” as each platform requires its own language.

I don’t mind learning a new language. I already jump from C++ to C# to Java to VBScript to Javascript to MS-SQL on a daily basis. For those of us who write code for a living, being multilingual is a job requirement. This is precisely why I care so very little about the supposed advantages of Swift; this isn’t a religious war for me, it’s just a tool. When someone comes out with a new kind of screwdriver, I may or may not buy it until I need it. And then I’ll just buy one and use it — I won’t try to convert all my screwdriver-toting friends.

So will PocketBible for OS X and/or iOS be re-written in Swift? Probably not today, and probably not until Apple requires it. But Swift depends on Objective-C under the hood, so my guess is that Apple will continue to support Objective-C apps for a long time.

Properties of Uninitialized Session Variables in ASP Classic

From time to time I have to remind myself of the following inscrutable facts, so here they are for posterity:

Given an uninitialized Session variable:

IsEmpty(Session("asdfasdf")) = True
IsNull(Session("asdfasdf")) = False
IsNumeric(Session("asdfasdf")) = True
Session("asdfasdf") = "" in an "if" statement returns True

This indicates that a completely uninitialized value appears to be both an empty string and has a numeric value (presumably zero). This is nonsense, especially given the next example.

Given a Session variable initialized to the empty string “”:

IsEmpty(Session("emptystring")) = False
IsNull(Session("emptystring")) = False
IsNumeric(Session("emptystring")) = False
Session("emptystring") = "" in an "if" statement returns True

In this case, we see that an empty string is not numeric. So to verify that a Session variable does not contain a numeric value, you need to also verify it contains a value at all, since empty Session variables appear to not be empty (i.e. they contain a numeric value of 0).

Craig 1, Hacker 0

When you purchase a product from our website, you click on a link to download it. The link appears to be legit — just a regular link to a file on our server. But it’s not. The file does not actually exist. We intercept the link and parse it to determine what to download to you.

When geeks like me see something like this, they poke around to see what they can find. When our server sees someone like me poking around, it sends me emails so I can watch them do it, because that’s what geeks like me like to do. No, we don’t tell the customer who’s doing the poking that we’re watching them. In fact, the error message they get tells them to forward the message to tech support. In reality we already know. 🙂

So here’s a customer from Australia doing some late-night hacking. He’s trying to download MyBible 5 for Palm OS without paying for it. Ironically, it’s free so if he really wants it, he can just go through the steps of ordering it and we’ll add it to his legitimate download account. But it’s more fun to try to get a free thing for free without not paying for it.

Two things you need to know: The product code for MyBible 5 is 3MBPGM005, and MyBible 3 is 3MBPGM002. The file name he’s trying to accidentally discover is “mb5setup.exe”. So this is the real link he’s trying to find: http://www.laridian.com/files/1158044/3MBPGM005/mybible5/program/mb5setup.exe

From the log:


http://www.laridian.com:80/files/1158044/3MBPGM005/mybible5/program/MyBibleSetup.exe
The filename requested (\mybible5\program\MyBibleSetup.exe) does not match the product (3MBPGM005). http://www.laridian.com:80/files/1158044/3MBPGM005/mybible5/program/MyBible5Setup.exe
The filename requested (\mybible5\program\MyBible5Setup.exe) does not match the product (3MBPGM005). http://www.laridian.com:80/files/1158044/3MBPGM005/mybible5/program/
The filename requested (\mybible5\program\) does not match the product (3MBPGM005). http://www.laridian.com:80/files/1158044/3MBPGM005/mybible5/program/MyBible51.exe
The filename requested (\mybible5\program\MyBible51.exe) does not match the product (3MBPGM005).

Next he tries to get an older version:


http://www.laridian.com:80/files/1158044/3MBPG3002/mybible3/program/MyBible3.exe
The filename requested (\mybible3\program\MyBible3.exe) does not match the product (3MBPG3002).

Back to looking for MyBible 5:


http://www.laridian.com:80/files/1158044/3MBPGM005/mybible5/program/MyBible5.exe
The filename requested (\mybible5\program\MyBible5.exe) does not match the product (3MBPGM005).

And back to MyBible 3:


http://www.laridian.com:80/files/1158044/3MBPG3002/mybible3/program/mb3setup.exe
The filename requested (\mybible3\program\mb3setup.exe) does not match the product (3MBPG3002). http://www.laridian.com:80/files/1158044/3MBPG3002/mybible3/program/MB3Setup.exe
The filename requested (\mybible3\program\MB3Setup.exe) does not match the product (3MBPG3002).

Here he gets it right! But he can’t download it because he doesn’t own it:


http://www.laridian.com:80/files/1158044/3MBPGM002/mybible3/program/mb3setup.exe
Customer 1158044 is not authorized to download product 3MBPGM002.

Now he switches his customer number to see if he can find a customer who *IS* authorized to download it. But he’s not going to get it without logging in as that customer first:


http://www.laridian.com:80/files/1158045/3MBPGM002/mybible3/program/mb3setup.exe
You are requesting files for customer 1158045 but customer 1158044 is logged in. You must access files through your download account. Exit your browser, then re-launch and go to our Login page to log in again.

Not sure what he’s doing here:


http://www.laridian.com:80/files/1158044/3MBPG3002/mybible3/program/mb3setup.exe
The filename requested (\mybible3\program\mb3setup.exe) does not match the product (3MBPG3002).

And at this point he admits defeat. Craig 1, hacker 0.

Implementing Interprocess Locking with SQL Server

I suppose everyone does this and I just haven’t heard about it. I don’t get out much, so it seems cool to me.

When we redesigned our company website (www.laridian.com) a couple years back, I needed a way to automatically update best-seller lists, new releases, and other dynamic data on the site without relying on an employee to do it every week/month/quarter. Initially, I considered writing a script that did this kind of thing and was launched by the OS on a schedule every so often, but I try to stay away from creating yet another little thing I’ll have to remember if we ever move the site or are forced to recreate it on another server.

So it occurred to me that I could keep track of when the last time was I had created a particular list or other piece of dynamic content on the site, and the first user who requests it after some time period (say once a month for “best sellers” and once a week for “new releases”) would cause the site to notice the content was old and regenerate it. That’s a cool idea on its own, but isn’t the subject of this article.

One of the problems I wanted to avoid was having two or three users who happened to show up at about the same time all trigger the process. I was concerned that it might be time-intensive and while I don’t mind delaying one customer while the data is created, I didn’t want to delay everyone who visits the site during those few seconds. So I came up with the idea of using SQL Server to implement a generic “lock” or “semaphore” capability I could use anywhere on the site.

The idea is to have a simple table with a Name field and a SetTime field. The Name field is given the UNIQUE constraint, so that duplicate records with the same Name field are not allowed. The first customer session that discovers it needs to rebuild the best-sellers list tries to INSERT a record with Name = ‘Best Sellers’ and SetTime = GETDATE(). If the INSERT succeeds, the process “owns the lock” and can do what it needs to do. If someone else comes along shortly thereafter and discovers it, too, needs to update the best-sellers list, it will try to do the same INSERT and will fail due to the existence of a record with the same Name field. This second process does not own the lock, and cannot update the best-sellers list. Instead, it uses the old list.

Once the first session has updated the list, it simply DELETEs the record, thus releasing its lock on the best-sellers list.

Since INSERT is an atomic operation there’s no possibility that two sessions are going to both believe they wrote the record.

Since the web is a flaky place, it’s necessary to allow for the possibility that a lock obtained a long time ago was never released. So every request for a lock checks the SetTime field. If the existing record is “too old” it is deleted before the attempt is made to INSERT the record.

This allows a certain amount of interprocess cooperation and communication between my Classic ASP pages with very little effort.

One of the side-effects is that the locks span not only all the processes running on the server, but can be made to span processes running on user devices. A recent use case that surfaced for this capability was the necessity of keeping a user from synchronizing his notes, highlights, or bookmarks from two (or more devices) with the Laridian “cloud” at the same time. The results can be unexpected loss of data on one or both of the devices.

The solution to this potential problem was for the synchronization process to request a lock that contains both the name of the table being synchronized and the customer ID. That way, many customers can synchronize, say, Bible bookmarks at the same time, but any one user can only synchronize one device at a time. This is a little more complicated than it seems, since PocketBible for Windows and PocketBible for iOS each have their own synchronization script on the server, while our newer clients (PocketBible for Android, Windows RT, and Windows Phone) use our new TCP-based synchronization server. The scripts for the older clients are written in Classic ASP and are invoked through HTTP POST operations from the client, while the new TCP server is written in C# and runs as a Windows Service. All have access to the same SQL Server database, and all implement the same locking strategy, which is working well.

In addition, during the debug process the TCP server runs on my local machine and connects via VPN to SQL Server. I can use and test the locking mechanism in this way before it goes live.

The combination of a very simple implementation using technology (SQL Server) that is well-known and well-tested, and the ability to implement locking across platforms makes this an interesting and (I would argue) elegant solution to a large number of problems.