ASP Classic: Simulating ASPCache

by Jon Davis 13. December 2008 06:36

This is for you folks who, like me, have found themselves having to deal with legacy ASP Classic code.

At my workplace, there are mountains of ASP files that make use of a component called ASPCache which when Googled looks like it comes from here: http://www.webgecko.com/products/aspcache/

This component appears in the global.asa like so:

<object runat="server" scope="application" id="Cache"
  progid="ASPCache" viewastext></object>	

Then, references to Cache appear throughout the code:

bAdded=Cache.Add(CacheKey, value, 60000) ' Cache for 1 minute	

.. and ..

myvar=Cache(cachekey)	

The code runs on two servers: a production server and a dev server. But for mainenance and new projects, I prefer to work locally. Since I did not have the ASPCache component, the entire site was crippled by the presence of this ASPCache dependency when I attempted to execute the web app from within my local instance of Windows XP + IIS 5.

Ignoring The Cache

The legacy code was already written in such a way that if ASPCache failed to take or to return a value it would work around it; the problem is that it didn't know how to live without the presence of the ASPCache dependency.

So the first thing I did to work around the problem was to just cripple the ASPCache dependency in global.asa.

REM #### obXXject runat="server" scope="application" id="Cache"
  progid="ASPCache" viewastext /obXXject	
	

.. and then wrap ASPCache invocations with error-ignoring handlers ..

On Error Resume Next ' no cache
    bAdded=Cache.Add(CacheKey, value, 60000) ' Cache for 1 minute
On Error Goto 0

..

On Error Resume Next
    myvar=Cache(cachekey)
On Error Goto 0

This worked fine for me locally; at this point I was able to execute my code.

But there were several files I had to apply this to, and the next problem is that there are about twenty different web sites with much of this same duplicate source code.

Worse--much worse--I had essentially killed off the ASPCache dependency from global.asa. I could never check in / commit this file back to Subversion without breaking the production codebase, unless I reversed these changes. It was frankly too easy to accidentally commit the entire site with all my changes including global.asa's removal of the dependency, and I didn't want this to happen.

So what I've decided to do was to reproduce the ASPCache API with my own COM object using the above-described interfaces.

Creating My Own ASPCache

To reproduce ASPCache, the requirements per the above samples are really quite simple:

  1. The <object> tag tells me that it needs a ProgID of "ASPCache". ProgIDs are to COM what DNS host names are to the Internet; they are mappings to CLSIDs (class IDs), which are GUIDs, like DNS host names map to IP addresses. The CLSIDs are registry keys that map back to a COM DLL, such as ASPCache's DLL.
  2. The COM interface needs to have a default function or property that returns an item when being supplied a key. Default properties / functions are an exclusive feature of VB6--they are not available in VB.NET, VBScript, or most other languages, but the behavior is not unlike an indexer in .NET that wraps a particular hashtable or function.
  3. The COM interface also needs an "Add" function that takes a key, the value to be cached, and a value indicating, in milliseconds, how long the item should remain in the cache.

This blog entry took me about 10x more time to document than it took me to create the ASPCache equivalent detailed herein; I love blogging. ;)

To produce this component, I fired up Visual Basic 6. Visual Basic 6 is still available for download on MSDN as far as I know, as it's still the only way to easily produce first-class COM objects without resorting to .NET CCW's, C++, or Delphi. I spent a few years in my past, back in the 90's and very early 2000's, working with VB 5/6 to create COM objects and Windows applications, so this was not foreign territory for me. At this point I'll walk through the steps as a tutorial.

  1. In the Visual Basic 6 New Project dialog, choose "ActiveX DLL".
    vb6_activex_dll_project
     
  2. The project that gets created is called "Project1", with a single item in the project called "Class1". Rename "Class1" to "ASPCache" using the Properties tool window on the right side of the screen.
  3. The project needs to be renamed as well. I changed it to "VirtualASPCache".

      
  4. In the class formerly named "Class1" (now "ASPCache"), I used the following code:

    Dim cacheCol As New Collection
    Dim cacheTmr As New Collection
    Dim m_flushenabled As Boolean
    Dim m_flushtimeout As Long
    Dim m_flushinterval As Long
    
    Public Property Get Item(key) As Variant
        Dim tmr
        On Error Resume Next
            tmr = cacheTmr(key)
        On Error GoTo 0
        If Not IsEmpty(tmr) And Not IsNull(tmr) Then
            If tmr < Now Then
                cacheTmr.Remove key
                cacheCol.Remove key
            End If
        End If
        On Error Resume Next
        Item = cacheCol.Item(key)
    End Property
    
    Public Property Let Item(key, ByVal vNewValue As Variant)
        cacheCol(key) = vNewValue
    End Property
    
    Public Function Add(key, value, Optional time_period As Long = 0) As Boolean
        Dim tmr
        If time_period = 0 Then time_period = 60000 * 60 * 30 ' 30 minutes
        tmr = DateAdd("s", (time_period / 1000), Now)
        On Error Resume Next
            cacheCol.Add value, key
            If Err.Number <> 0 Then
                On Error GoTo 0
                Add = False
                cacheCol.Remove key
                cacheCol.Add value, key
                On Error Resume Next
                    cacheTmr.Remove key
                On Error GoTo 0
                cacheTmr.Add tmr, key
                Exit Function
            End If
        On Error GoTo 0
        cacheTmr.Add tmr, key
        Add = True
    End Function
    
    ' ignored
    Public Property Get FlushEnabled() As Boolean
        FlushEnabled = m_flushenabled
    End Property
    
    ' ignored
    Public Property Let FlushEnabled(ByVal enabled As Boolean)
        m_flushenabled = enabled
    End Property
    
    ' ignored
    Public Property Get FlushTimeout() As Long
        FlushTimeout = m_flushtimeout
    End Property
    
    ' ignored
    Public Property Let FlushTimeout(ByVal Timeout As Long)
        m_flushtimeout = Timeout
    End Property
    
    ' ignored
    Public Property Get FlushInterval() As Long
        FlushInterval = m_flushinterval
    End Property
    
    ' ignored
    Public Property Let FlushInterval(ByVal Timeout As Long)
        m_flushinterval = Timeout
    End Property
    
    
    
    
  5. Make the Item property the default property. To do this, hit F2 to open up the Object Browser. Then at the top left drop-down menu, filter by your project. Select ASPCache, then on the right find Item. Right-click on it, choose Properties, and then expand the Advanced view. On the left is a Procedure ID drop-down where you can specify "Default".
    vb6_default_property
  6. Save the project (File -> Save Project)
  7. Make the DLL (File -> Make VirtualASPCache.dll)
  8. In a command prompt (Windows Start menu -> Run... -> cmd.exe) navigate to your project ("cd \myprojects\virtualaspcache", etc) and register the DLL to the COM registry:
    regsvr32 VirtualASPCache.dll
  9. Almost there. Now we need to change the ProgId. VB6 uses the following convention for ProgID: "{ProjectName}.{ClassName}". So for us that would be "VirtualASPCache.ASPCache". So, pull up Registry Editor (Windows Start menu -> Run... -> regedit.exe). Under HKEY_CLASSES_ROOT, find a key (keys look like directories on the left; values are on the right) called "VirtualASPCache.ASPCache".


    Rename "VirtualASPCache.ASPCache" to "ASPCache".  

Restoring the original code in global.asa, not only am I able to retain the dependency reference, the cache API actually works, it is not merely walked over. So now I can retain all the original ASP code as-is, and it should all function pretty much the same.

Download: VirtualASPCache.zip (8.31 kb)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Web Development

Strange Fun In The Land Of Legacies

by Jon Davis 10. December 2008 18:41

That was a fun title. Anyway...

At my job in addition to .NET bits I have been dutifully tasked with ongoing maintenance and updates projects for systems still running ASP Classic. That is, raw VBScript, with no MTS, no VB6 code reuse, just pure vanilla VBScript and ADO Classic. Mind you, I *can* use jQuery and the system does use SQL Server 2005. But I never dreamed I would ever say this--not in the eight years or so it's been since I abandoned ASP Classic and adopted Java and then .NET. Gosh, I'm still scratching my head wondering how it happened...

.. but I'm actually enjoying this. I'm enjoying VBScript. You heard me say it, right here. That lame language that has been given all kinds of names (a three-letter one that starts with a 'g' comes to mind), I am actually having fun with it.

I think I know why, too. After spending several days working with VBScript, I go back to C# or even JavaScript and realize that the transition from VBScript to C# is like the transition from C# to C++: in exchange for raw power, you have to do a lot more futzing with syntax to accomplish something.

In VBScript, a lot of time is saved by relying on variants and dynamic typing as such. Mind you, I'm fully aware of why the Variant was thrown out in .NET and how it is so evil in computer science. The fact that I don't have typed objects makes me pull my hair out more than it gives me pleasure. But this is script, and it almost forces me to be productive by refusing to get too formal. There's a difference between software and script, and it has nothing to do with technology.

I used to argue that what makes a "script" was that it isn't script if it doesn't run straight from the source code, which I think is close. Microsoft's definition of "script", after abandoning the Active Scripting runtime, was dynamic typing (which I think is also close, but also wrong). I've come to realize another differentiater: business use. A script is a piece of code that just gets the local, immediate, and temporary job done. A form is a classic scenario. A visitor to a web page fills out a form, the data gets collected to a database, the user is told "thanks, we'll get back to you, now go away", and a whole lot of nothing else happens in the short term. And in most business cases, you only have a tiny handful of people (like, three, at the most) filling out this form at the same time, so you don't need to worry a huge amount about performance. (If this was a page that was getting pounded on, you can scale up or scale out, hardware-wise. I am not solely impressed with people who go nuts about optimizing their systems for hundreds of thousands of hits per day but only keep two web servers up. Ten web servers on a farm and it's still slow, okay, then there's a problem with the code, or maybe the networking, otherwise, I mean, seriously. Mosso.com FTW.) 

VBScript is very much a command-oriented language. Parentheses for function invocations are discouraged to make a more "command-like" syntax of "mow lawn" rather than "mow(lawn)". This doesn't account for or mean much, but after abandoning C# for a short period of a few days and spending a few days in VBScript I take a step back and realize how nice it is to be terse in my command-orientation; no futzing with parentheses and semicolons and brackets.

In my opinion, the best development language is the one that a) gets the job done reliably and effectively, b) is most productive for the developer (effort+time=productivity loss), and c) is most maintainable. VBScript is not the most stable language but it does get the job done, and on simple forms pages it is a great language. It is VERY productive, albeit it depends on what you're doing with it as there's no templating or scaffolding in ASP Classic, but I still admire ADO Classic at times. And last, on the maintainability side .... *cough* well .. no. It's not very maintainable. On the other hand, it all really depends on how you write your old code. Componentizing your code in #include's and using VBScript classes and functions where appropriate, one can produce maintainable code. I think where it gets messy is in the details. I still, always have, and always will, hate "Then", as in "If...Then". What a waste of four letters, two if you were to replace it with parentheses in other languages. And when you're using it inline with markup, you also can't leave out the "End If". It's usually not an issue, until you do something like ...

	<input type="radio" <% If Request("mySel")="ABC" Then%> checked="checked"<%End If%> 
	name="mySel" id="mySel_dah" value="ABC" />	
	

Ugly. This is why ASP.NET Web Forms was invented, to bury that logic into the ViewState. Personally, though, I think the trade-off isn't there, and in fact, kudos to my boss, who went around asking what we thought about him making the rule, "No ASP.NET Web Forms on public-facing pages, period. Use inline code or controls that don't require runat=server forms." 

But despite the despicable fact that you can't initialize your variables when you declare ("Dim") them, I do really like the fact that everything is convertible. For example, you can always compare Request("xxx") to "" (empty string) because it automatically does a CStr() behind the scenes, which also converts Null. It's just less coding that way.

You do have to know what you're doing, and what the language interpreter is doing under the covers, to keep from making the messy language that VBScript already is into a sloppy codebase. Most of the horrors of VBScript in times past come from the fact that it was so approachable and easy to write code with that people who didn't know how to code made a big mess everywhere they went. You had crap lying around everywhere. And nobody likes smelling or cleaning that stuff up.

But the alternative of a strongly typed language, particularly when one considers Web Forms, doesn't look a whole lot better. You end up with a huge amount of boilerplating and architectural, lifecycle bloat that you typically don't even need.

I don't know why Microsoft hasn't come up with VBScript.NET, a VB-oriented scripting language, with fully dynamic typing, that runs on the CLR. But I'm not shedding any tears. They did adopt IronRuby, et al.

These things said, would someone please send me ASP.NET MVC weekend/moonlighting work?! I can only take so much of this "g" stuff uninterrupted!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Web Development


 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  October 2018  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar