As far as long-term code maintenance is concerned, ASP could be one of the worst environments ever created. I mean, here we have tons of code where logic is barely separated from content and sometimes not at all. Corporations literally are stuck with millions of lines of hard-to-follow ASP code and they will be paying for it in years to come. Which is great for developers, because it keeps us in business. And don’t get me started on debugging ASP. As one of my co-workers says: "You have to dance on one leg and sing a Gypsy song in order to get Interdev to debug ASP code". So, when Paul Sheriff of PDSA (a well-know VB instructor - see his ad in every VBPJ) showed me a working, easily maintainable web application, totally written in VB (not VBScript), except for 6 lines of ASP code, I was stunned. I had to go through several steps of ASP recovery workshop. First there was bewilderment - why, why, why did I program ASP all this time. Then anger - so much lost time that I will never get back. After anger I experienced curiosity - exactly how does this work and what more can be done with it. The 4th step was acceptance. In the final step I embraced the new paradigm. Now I am more productive than ever. Web applications fly off my computer.
So, how does this work? Let us start with basic facts. You will need one VB project. You will need a separate class for each page. That sounds like a lot of work especially if you have a large site. However, these classes are fairly small with no properties, one method and only several functions. Moreover, the classes are similar to each other, so there is going to be a lot of cut & paste. You will also need one ASP file. As this is where the users first touch your web site, it is here where we start to code. Create a file called exec.asp and add lines below to it.
set oInstance = Server.CreateObject(sProject & "." & sClass)
oInstance.ProcessRequest
set oInstance = nothing
%>
From here on out, that is all ASP you will ever have to write. Say goodbye to it, spit on it, cuss at it and generally get all the anger out of your system before proceeding.
Let's analyze what this ASP script is doing. It expects two items in the QueryString collection: cls and prj. Then it puts them together to form a class string and attempts to create an object. So for instance, if you type in on the browser line:
the ASP file assumes that you have a COM DLL with a class string called
MyProject.View.
Futher down, notice line oInstance.ProcessRequest. The ASP file assumes that the created object has a Public method called ProcessRequest. So obviously all the processing will go on in ProcessRequest method.
So far, so good - we need to create a class for each page and there should be a standard Public method called ProcessRequest. Easy enough.
You maybe wondering at this point - how exactly am I going to get access to the ASP intrinsic objects? Well, because our COM object is being created with Server.CreateObject instead of CreateObject, the intrinsic objects will be available (with a little bit of code) in their full glory.
Now create a new ActiveX DLL project. Call the project "MyProject". Call the class "View".
Add the following to the class (you will have to add this to every single class that will be created the ASP script).
Option Explicit
Private mobjContext As ASPTypeLibrary.ScriptingContext
Private Const conHTMLTokenPrefix = "{|"
Private Const conHTMLTokenSuffix = "|}"
Public Sub OnStartPage(objContext As ASPTypeLibrary.ScriptingContext)
Set mobjContext = objContext
End Sub
Public Sub OnEndPage()
Set mobjContext = Nothing
End Sub
Public Sub ProcessRequest()
On Error GoTo errProcessRequest
'Your code goes here
'...
exitPoint:
'generic exit point
Exit Sub
errProcessRequest:
'write out the error to the browser
Call mobjContext.Response.Write(Err.Description)
GoTo exitPoint
End Sub
Let's analyze this piece of code. The way ASP's Server.CreateObject works is that it looks for OnStartPage and OnEndPage methods to pass in the ASP context object (intrinsic objects, in other words). Once ASP passes in the object context intrinsic objects (Request, Response, Server, Application, Session) are available under the mobjContext super object. For instance, to write out a line of code you would use the following in the ProcessRequest sub.
mobjContext.
Response.Write
"My Test Line"
Pretty cool, ha? Keep in mind, you can place breakpoints in your ActiveX DLL and use the full power of the Visual Basic debugger.
Now you are probably thinking, the great thing about ASP was that I could create HTML page in my favorite editor and then intersperse VBScript code into it. Do I have to write HTML out one line at a time using this approach? Absolutely not - it would defeat the whole purpose of Rapid Application Development. In fact, our approach is relatively easy to implement.
We can achieve this ease by use of HTML templates. Let me explain. Let's say we have a page that has to be populated by the list of employees. So our final HTML page would look like this:
Obviously, we can't just send out a hard-wired list, as our employee list may change. So we then create a template. We replace the hardwired names with a tag, our engine will understand.
So in our code, we will get the list of employees from the database, read in this template, replace the
EMPLOYEE LIST tag with our real employee list and send the text out using mobjContext.Response.Write method. Easy
enough? But wait, there is more. You can wrap the whole process of reading
templates and replacing tags into a reusable class (one is provided with the
download).
So here is how the final draft of the ProcessRequest method will look like:
Public Sub ProcessRequest()
Dim objTemplate As clsTemplateManager
Dim strReturn As String
'******************************************
'normally this code should be encapsulated
'in another object or module
Dim oConn As New ADODB.Connection
Dim oRs As New ADODB.RecordSet
Dim sList As String
'open a connection to an Access database
oConn.Open Provider="Microsoft.Jet.OLEDB.3.51;;Data Source=" & _
App.Path & "\Sample.mdb"
'populate the recordset with the names
oRs.Open "select * from tblNames", oConn
'build an HTML list
Do While Not oRs.EOF
sList = sList & "<option value=" & oRs!ID & ">" & _
oRs!Name & "</option>"
oRs.MoveNext
Loop Set oRs = Nothing
Set oConn = Nothing
'******************************************
'Template Manager
Set objTemplate = New clsTemplateManager
With objTemplate
'Add values and Tokens
.TokenAdd sList, "EMPLOYEE LIST"
'Set Template Name and Tokens
.TemplatePath = App.Path & "\Templates" & "\SeeName.htm"
.TokenPrefix = gconHTMLTokenPrefix
.TokenSuffix = gconHTMLTokenSuffix
'Combine tokens and template and return HTML page
strReturn = .WriteResponse
End With
' Write out the page
Call mobjContext.Response.Write(strReturn)
exitPoint:
'generic exit point
Exit Sub
errProcessRequest:
'write out the error to the browser
Call mobjContext.Response.Write(Err.Description)
GoTo exitPoint
End Sub
Here is a quick explanation on how to get this Setup working on your computer.
I assume you have IIS or PWS - if you don't get it at
Then under c:\inetpub\wwwroot folder create a new
directory called Sample. Unzip the downloaded project into this directory.
Double-click on the MyProject.vbp file and click Run.
That's all there is to it. Now you can place
breakpoints in your VB code, learn how the code works and debug at will. The
templates are location in the \Sample\Templates folder
What does the downloaded program do? Included with the download there is a simple access database With a single table. This table contains a list of employees. This application allows to manipulate the names of employees.
Troubleshooting
First and foremost, read IIS and COM Troubleshooting guide.
It pretty much covers all the possible situations and how to get out of them.
Are you getting Server.CreateObject failed while checking permissions. Access is denied to
this object error or some other permission error? These problems occur mostly (99%) with Windows 2000 or
Windows XP Server. They are documented (and several solutions are presented) in Microsoft Knowledge Base
article Q259725.
Basically, after being severely critisized for lack of security in Windows NT, they really went overboard with
it in Windows 2000 and later. For instance, out of the box, you can't debug ActiveX DLLs in Visual Basic 6.
You can run them fine, but you can't debug them.
Why does this occur? Because when you debug an ActiveX DLL which is
being called from IIS, you are doing so with the IUSR_machinename account. Now, IUSR_machinename account has permissions to
run ActiveX DLL, but when you are debugging in the VB IDE, you are really running VB6.EXE which is actually an ActiveX EXE.
IUSR_machinename account has no permissions to run ActiveX EXEs. Permissions for ActiveX EXEs are set through DCOM.
The knowledge base article presents 2 solutions. First solution is a long-term resolution which will allow you to
debug any ActiveX DLLs. Second solution will only apply to your current problem. On top of that if your web server is
available to the public - it will make everyone log on(!!!), so try the first solution and see if that resolves your problem.
I actually have an even better and an easier solution to this problem. Simply run your app under an account that has
admin rights. What? You are worried about security? This is your development PC, so who cares about security.
Once you get past the initial amazement :), you'll want to speed up your development even further. In my work,
I have to constantly crank out variuos lists of information, often in a listbox form. So I wrote a control that
almost fully emulates the standard VB listbox and adds a Render method that spits out the list in the
HTML listbox form. Check it out here.
Happy code writing.
Update
P.S. A reader emailed me complaining that I haven't listed any of the drawbacks of this method. And as with anything,
there are tradeoffs to make in life, so there are some drawbacks. I will list them, each with a quick explanation.
Performance
This method yields pretty good performance. Most of the time it is better than ASP or ASP plus some COM Objects. However,
don't expect to write the next Yahoo site because the performance will not stand up to it.
On a standard server (512 MB RAM, fast HD, PIII 700Mhz or so), you will get pretty good performance with around
30 concurrent (by concurrent I mean people simultaneously requesting a page) connections.
The overhead of simultaneously retrieving 30 different HTML templates from the hard drive
(or even from RAM once it becomes cached) is enough to bog down the performance. However, once you reach a milestone where
you have 30 concurrent users, you should scale out (i.e. get more servers).
Security
Another common complaint is that the method exposes class names and that somehow it is dangerous. While I don't
consider it a risk, if it makes you uncomfortable, simply mask the class names by changing then in the ASP file. In
other words, if the class name is EmployeeDelete, have the URL pointing to exec.asp?cls=Employee&CMD=Delete.
In the ASP file, you would have to catch this (using the QueryString collection) and do a Server.CreateObject
on QueryString("cls") & QueryString("CMD").
Hosting
Jason (jason@catamaranco.com) mentioned this problem and it truly is the only one that actually impedes this method.
If you are hosting your application with a hosting provider, chances are they won't allow you to register DLLs on
their box. This is not a problem if you are writing your apps for the intranet or for corporations that host
their own web sites. So what are you choices if you are in a hosting situation? Well, you can co-locate your
own box with the hosting provider. That's more expensive, but the only solution I can think of.