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:
-
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.
-
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.
-
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.
-
In the Visual Basic 6 New Project dialog, choose "ActiveX DLL".
-
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.
-
The project needs to be renamed as well. I changed it to "VirtualASPCache".
-
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
-
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".
-
Save the project (File -> Save Project)
-
Make the DLL (File -> Make VirtualASPCache.dll)
-
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
- 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)