Classic ASP can be written totally modular using WSC (Windows Script Components) AKA scriptlets.
These are files, written in VBscript or Jscript, that have properties and methods defined, and in that way they can be used just like COM components. The advantage is you don't have to register them or restart IIS after changing the code inside of them.
This is an example WSC (please note that this is untested, it is not fully functional and it might contain errors). Copy-paste this code in your ASP site somewhere and call it debugging.wsc:
<?xml version="1.0"?>
<component>
<?component error="true" debug="true"?>
<registration
description="debugging"
progid="debugging.WSC"
version="1.00"
classid="{AE434F7F-C64E-46D4-A103-FF3D47B05A91}"
>
</registration>
<public>
<!-- PROPERTIES -->
<property name="executionTime">
<get/>
</property>
<property name="logpath">
<get/>
<put/>
</property>
<!-- METHODS / PUBLIC FUNCTIONS -->
<method name="log">
<PARAMETER name="inputValue"/>
</method>
<method name="send_mail">
<PARAMETER name="who"/>
<PARAMETER name="subject"/>
<PARAMETER name="message"/>
</method>
<method name="hellofromjscript">
<PARAMETER name="name"/>
</method>
<method name="getPersonByName">
<PARAMETER name="name"/>
</method>
<method name="open"></method>
<method name="close"></method>
</public>
<implements type="ASP" id="ASP"/>
<script language="VBScript">
<![CDATA[
' here is where global variables go
' every property has a corresponding global varibale
dim executionTime, logpath
dim fs, WriteFile
' start a timer every time this component is loaded
' this way we can debug the running time at various places in the ASP page
dim timeStart : timeStart = Timer()
'getters and setters for the properties'
function get_executionTime()
get_executionTime = FormatNumber((Timer()-timeStart),4,,,0)
end function
function get_logpath()
get_logpath = logpath
end function
function put_logpath(newValue)
logpath = newValue
end function
' *************************************************
' * PUBLIC FUNCTIONS *
' *************************************************
function log(message)
' make sure the path you are logging to has 'modify' rights for the user of the current application pool in IIS
' determine path to the log file
dim LOG_FILEPATH : LOG_FILEPATH = logpath & year(date)
if month(date) < 10 then
LOG_FILEPATH = LOG_FILEPATH & "0" & month(date)
else
LOG_FILEPATH = LOG_FILEPATH & month(date)
end if
LOG_FILEPATH = LOG_FILEPATH & filename & ".log"
' create the file if it doesn't exist
If NOT fs.FileExists(LOG_FILEPATH) Then
call fs.OpenTextFile(LOG_FILEPATH, 2, True, 0)
Set WriteFile = fs.OpenTextFile(LOG_FILEPATH, 2, True, 0)
call WriteFile.WriteLine("Logfile Created " & currentDateTime)
WriteFile.Close()
Set WriteFile = Nothing
End If
' write the log line
Set WriteFile = fs.OpenTextFile(LOG_FILEPATH, 8, True, 0)
call WriteFile.WriteLine(message)
WriteFile.Close()
Set WriteFile = Nothing
end function
function send_mail(who,subject,message)
' *******************************************************************'
' * E-MAIL A MESSAGE *'
' *******************************************************************'
' You need some variables in the application scope to use this method
' A WSC can access the standard ASP objects like server, request, application, session etc.
' because we included the <implements type="ASP" id="ASP"/> tag at the top.
if hasValue(who) Then
Dim myMail
Set myMail = CreateObject("CDO.Message")
myMail.Subject = subject
myMail.From = application.contents("DefaultEmailSender")
myMail.To = who & " <" & who & ">"
myMail.TextBody = message
myMail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
myMail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = Application.contents("MailServer")
myMail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
myMail.Configuration.Fields.Update
myMail.Send
set myMail = nothing
end if
End Function
function getPersonByName(name)
' It is also possible to return an object from a WSC
' just SET it in the return value and also SET it in the code that calls the function:
' Please note that this function not functional, just an example of what can be accomplished.
sqlstatement = "DECLARE " &_
"@username varchar(50) " &_
"SELECT " &_
"@username = " & parameter.string(name) & " "
' parameter could be a WSC that parses user inputs and makes sure the application is protected
' against SQL injection attacks
sqlstatement = sqlstatement & "SELECT * "
sqlstatement = sqlstatement & "FROM Users "
sqlstatement = sqlstatement & "WHERE username = @username "
' DataAccessLayer could be another WSC that handles database connections and logs queries for example
set getPersonByName = DataAccessLayer.select(sqlstatement)
end function
' just an internal helper function.
' because it is not defined in the XML part at the top, this function can only be used internally
function hasValue(Val)
hasValue = NOT(isNull(Val) OR Val="")
end function
'standard methods for opening and closing more objects - these can be extra WSC's as well'
function open()
Set fs = CreateObject("Scripting.FileSystemObject")
logpath = Server.Mappath("/") & "\logs\\"
end function
function close()
Set fs = Nothing
end function
]]>
</script>
<script language="javascript">
<![CDATA[
function hellofromjscript(name){
// Jscript works aswell. Keep in mind that Jscript is case-sensitive and
// that you will need to address the full ASP object, I.E. Application.Contents("myvar") instead of just Application("myvar")
Response.Write("Hello " + name);
};
]]>
</script>
</component>
A WSC can be used from ASP like this (provided you have the WSC in a directory called wscs):
<%
dim debugging, recordset
Set debugging = GetObject("script:"&Server.MapPath("/wscs/debugging.wsc"))
debugging.open()
debugging.log("Hello from a WSC")
' you can return objects like recordsets from a WSC:
set recordset = getPersonByName("bill")
set recordset = Nothing
debugging.close()
Set debugging = Nothing
%>
Using WSCs it is possible to load functionality conditionally (unlike include files, which are included always).
It's possible to program using N-tier construnctions, as WSCs can load other WSCs.
You can find an example project that contains the use of WSCs and lots more helpful stuff on keeping an old ASP 3.0 application running on https://gitlab.com/erik4/classic-asp-book-examples