Figure 2 The Browser's Main Form

Option Explicit

' Global objects
Private m_asp As CAspParser

' Function Declaration
Private Declare Function PathIsURL Lib "shlwapi.dll" Alias "PathIsURLA" _
(ByVal strPath As String) As Boolean

Private Sub cmdGo_Click()
    LocalNavigate URL.Text
End Sub

Private Sub LocalNavigate(ByVal strURL As String)
    Dim strStdOutFile As String
    
    ' Set the name of the output HTML file
    strStdOutFile = App.Path & "\asp-local.htm"
        
    ' Navigate only if it is a URL...
    If PathIsURL(strURL) Then
        WB.Navigate strURL
        Exit Sub
    End If
    
    ' Local ASP file...
    Dim bOK As Boolean
    bOK = m_asp.ParseTextToFile(strURL, strStdOutFile)
    If bOK = False Then
        MsgBox "An error occurred. Check the file name"
        Exit Sub
    End If
        
    ' ————————————————————————————
    ' REFRESH THE USER INTERFACE
    ' ————————————————————————————
        
    ' Refresh the UI: the ASP textbox
    Dim buf As String
    Dim fso As Object, f As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set f = fso.OpenTextFile(URL.Text)
    buf = f.ReadAll()
    f.Close
    ASP.Text = buf
    
    ' Refresh the UI: the HTML textbox
    Set f = fso.OpenTextFile(strStdOutFile)
    buf = f.ReadAll()
    f.Close
    HTML.Text = buf
    
    ' Refresh the UI: the WebBrowser
    WB.Navigate strStdOutFile
End Sub

Private Sub Form_Load()
    WB.Navigate "about:blank"
        
    ' Create an instance of the ASP Offline Parser
    Set m_asp = New CAspParser
    m_asp.SetScriptControl SC
    m_asp.Initialize
End Sub

Private Sub Form_Resize()
    Dim screenWidth As Integer, screenHeight As Integer
    Dim offset As Integer
    
    offset = 100
    screenWidth = Main.ScaleWidth - 2 * offset
    screenHeight = Main.ScaleHeight - WB.Top - offset
    
    ' WebBrowser
    WB.Left = offset
    WB.Width = screenWidth / 2
    WB.Height = screenHeight
    
    ' ASP Textbox
    ASP.Left = WB.Left + WB.Width
    ASP.Width = screenWidth / 2
    ASP.Height = screenHeight / 2
    
    ' HTML Textbox
    HTML.Left = WB.Left + WB.Width
    HTML.Top = ASP.Top + ASP.Height
    HTML.Width = screenWidth / 2
    HTML.Height = screenHeight / 2
End Sub
Figure 3 Common ASP Objects
Object
Description
Response
Allows you to send code out to the browser
Request
Contains information about the browser's status
Server
Carries out processing activities on the server
Session
Allows you to create and maintain the state of the Web application on a per-user level
Application
Allows you to create and maintain the global state of the Web application
Figure 4 CAspParser
Option Explicit

' Internal variables
Private m_objResponse As Object
Private m_objScriptCtl As ScriptControl

' Constants
Private Const ASP_INITCODE = "<%"
Private Const ASP_ENDCODE = "%>"

' SetScriptControl : Receive and store the instance
'                  : of the ScriptControl to work with
'——————————————————————————————
Public Sub SetScriptControl(ByVal oSC As ScriptControl)
    Set m_objScriptCtl = oSC
End Sub


' Initialize : Manage to make the class ready for parsing
'            : the ASP script code blocks
'——————————————————————————————
Public Function Initialize() As Boolean
    If m_objScriptCtl Is Nothing Then
        Exit Function
    End If

    ' Initialize the Script Control
    m_objScriptCtl.Language = "VBScript"
    
    ' Populate the script's namespace with fake ASP objects
    Set m_objResponse = CreateObject("MyASP.Response")
    m_objScriptCtl.AddObject "Response", m_objResponse
    '
    ' TODO: add other objects here
    '
End Function


' ParseTextToFile : Extract the <%..%> code blocks and process
'                 : them separately through the Script Control
'——————————————————————————————
Public Function ParseTextToFile(ByVal aspFile As String, _
ByVal strFile As String) As Boolean

    Dim fso As Object, f As Object
    Dim strBuf As String
    
    ' Check against the ASP extension
    If Not CheckInputFile(aspFile) Then
        ParseTextToFile = False
        Exit Function
    End If
        
    ' Read the ASP file into memory
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set f = fso.OpenTextFile(aspFile)
    strBuf = f.ReadAll()
    f.Close
  
    ' Clear the Response's buffer
    m_objResponse.Clear
    
    ' Walk through the text and isolate the code blocks
    Do
        ' Search for the opening tag "<%"
        Dim nStartPos As Integer, nEndPos As Integer
        nStartPos = InStr(1, strBuf, ASP_INITCODE, vbTextCompare)
        
        ' If no tag is found, then output the remaining text
        If nStartPos = 0 Then
            m_objResponse.Write strBuf
            Exit Do
        End If
        
        ' If <% has been found, output the preceding text through Response
        m_objResponse.Write Left(strBuf, nStartPos - 1)
        
        ' Move the pointer forward by the length of "<%"
        nStartPos = nStartPos + Len(ASP_INITCODE)
        
        ' Search for the closing tag "%>"
        nEndPos = InStr(nStartPos, strBuf, ASP_ENDCODE, vbTextCompare)
        
        ' If no tag is found, then error
        If nEndPos = 0 Then
            ParseTextToFile = False
            Exit Function
        End If
        
        ' Grab the script text
        Dim strScript As String
        strScript = Mid(strBuf, nStartPos, nEndPos - nStartPos)
        
        ' Resolve possible ambiguities
        strScript = ResolveAmbiguity(strScript)
        
        ' Execute the script
        m_objScriptCtl.ExecuteStatement strScript
        
        ' In case you called Response.End...
        If m_objResponse.CanContinue = False Then
            Exit Do
        End If
        
        ' Move the pointer forward by the length of "%>"
        strBuf = Mid(strBuf, nEndPos + Len(ASP_ENDCODE))
    Loop
       
    ' Save to file
    Set f = fso.CreateTextFile(strFile)
    f.Write m_objResponse.ResponseBuffer
    f.Close
    Set f = Nothing
    Set fso = Nothing

    ParseTextToFile = True
End Function

' ResolveAmbiguity : Modify the script code before execution to
'                  : avoid possible misunderstandings. For
'                  : example, <%=x%>. Here x becomes Response
'——————————————————————————————
Private Function ResolveAmbiguity(ByVal strScript As String) As String
    ' If the string begins with = it is translated to
    ' Response.Write
    
    ResolveAmbiguity = strScript
    If Left(strScript, 1) = "=" Then
        ResolveAmbiguity = "Response.Write " & Trim(Mid(strScript, 2))
    End If
End Function



' CheckInputFile : Verify whether the specified file is
'                : an ASP file
'——————————————————————————————
Private Function CheckInputFile(ByVal aspFile As String) As Boolean
    
    ' Verify it is an ASP file
    CheckInputFile = True
    If InStr(1, aspFile, ".asp", vbTextCompare) = 0 Then
        CheckInputFile = False
        Exit Function
    End If
    
End Function

Private Sub Class_Initialize()
    Set m_objScriptCtl = Nothing
End Sub
Figure 5 MyASP.Response
// Response.h : Declaration of the CResponse

#ifndef __RESPONSE_H_
#define __RESPONSE_H_

#include "resource.h"       // main symbols

///////////////////////////////////////////////////////////////////////////
// CResponse
class ATL_NO_VTABLE CResponse : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CResponse, &CLSID_Response>,
    public IDispatchImpl<IResponse, &IID_IResponse, &LIBID_MYASPLib>
{
public:
    CResponse()
    {
        m_responseBuffer.Empty(); 
    }

DECLARE_REGISTRY_RESOURCEID(IDR_RESPONSE)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CResponse)
    COM_INTERFACE_ENTRY(IResponse)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IResponse
public:
    STDMETHOD(Clear)();
    STDMETHOD(get_ResponseBuffer)(/*[out, retval]*/ BSTR *pVal);
    STDMETHOD(Write)(/*[in]*/ BSTR bstrText);
private:
    CComBSTR m_responseBuffer;
};

#endif //__RESPONSE_H_


// Response.cpp : Implementation of CResponse
#include "stdafx.h"
#include "MyASP.h"
#include "Response.h"

///////////////////////////////////////////////////////////////////////////
// CResponse

// Response.h : Declaration of the CResponse

#ifndef __RESPONSE_H_
#define __RESPONSE_H_

#include "resource.h"       // main symbols

///////////////////////////////////////////////////////////////////////////
// CResponse
class ATL_NO_VTABLE CResponse : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CResponse, &CLSID_Response>,
    public IDispatchImpl<IResponse, &IID_IResponse, &LIBID_MYASPLib>
{
public:
    CResponse()
    {
        m_responseBuffer.Empty();
        m_bCanContinue = true;
    }

DECLARE_REGISTRY_RESOURCEID(IDR_RESPONSE)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CResponse)
    COM_INTERFACE_ENTRY(IResponse)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IResponse
public:
    STDMETHOD(get_CanContinue)(/*[out, retval]*/ BOOL *pVal);
    STDMETHOD(End)();
    STDMETHOD(Clear)();
    STDMETHOD(get_ResponseBuffer)(/*[out, retval]*/ BSTR *pVal);
    STDMETHOD(Write)(/*[in]*/ BSTR bstrText);
private:
    CComBSTR m_responseBuffer;
    BOOL m_bCanContinue;
};

#endif //__RESPONSE_H_



// Response.cpp : Implementation of CResponse
#include "stdafx.h"
#include "MyASP.h"
#include "Response.h"

///////////////////////////////////////////////////////////////////////////
// CResponse

STDMETHODIMP CResponse::Write(BSTR bstrText)
{
    m_responseBuffer.AppendBSTR(bstrText);

//    Use this MessageBox to see what's going on step by step
      MessageBoxW(0, bstrText, 0, 0);

    return S_OK;
}

STDMETHODIMP CResponse::get_ResponseBuffer(BSTR *pVal)
{
    // There's no equivalent method/property in the ASP object model.
    // In ASP you cannot access the buffer being sent to the browser

    m_responseBuffer.CopyTo(pVal);

    // Clear the buffer, like you're using Flush()
    m_responseBuffer.Detach(); 
    return S_OK;
}

STDMETHODIMP CResponse::Clear()
{
    // Reenable processing
    m_bCanContinue = true;

    m_responseBuffer.Detach(); 
    m_responseBuffer.Empty(); 
    return S_OK;
}

STDMETHODIMP CResponse::End()
{
    m_bCanContinue = false;
    return S_OK;
}

STDMETHODIMP CResponse::get_CanContinue(BOOL *pVal)
{
    // Since the browser manages the Response.End we need a way
    // to make it know we've called it

    *pVal = m_bCanContinue;
    return S_OK;
}
Figure 8 A Data-driven ASP Page
<html>
<link rel="stylesheet" href="tablestyles.css">
<body>

<%
    strConn = ""
    strConn = strConn & "Provider=Microsoft.Jet.OLEDB.4.0;"
    strConn = strConn & "Data Source=d:\my documents\articles.mdb"

    set oRS = CreateObject("ADODB.Recordset")
    oRS.CursorLocation = 3
    oRS.Open "select title, abstract from articles", strConn
    set oRS.ActiveConnection = Nothing
%>

<table class=defTable>
<thead class=defHead>
<tr><td>Title</td><td>Abstract</td></tr>
</thead>
<%
    while Not oRS.EOF
        Response.Write "<tr>"
        Response.Write "<td class=defItem>"
        Response.Write oRS("title")        
        Response.Write "</td>"
        Response.Write "<td class=defItem>"
        Response.Write oRS("abstract")        
        Response.Write "</td>"
        Response.Write "</tr>"

        oRS.MoveNext
    wend
%>

</table>

</body>
</html>
Figure 12 Redirecting a Local ASP Link
Private Sub WB_BeforeNavigate2( _
    ByVal pDisp As Object, _
    URL As Variant, _
    Flags As Variant, _
    TargetFrameName As Variant, _
    PostData As Variant, _
    Headers As Variant, _
    Cancel As Boolean)

    ' If it is a URL, do as usual
    If PathIsURL(URL) Then
        Exit Sub
    End If
    
    ' If it is not a local ASP page, do as usual
    If InStr(1, URL, ".asp", vbTextCompare) = 0 Then
        Exit Sub
    End If
    
    ' —————————————————
    ' If here, it is a local ASP page
    ' —————————————————
    
    ' Redirect the link
    LocalNavigate URL
 
    ' TODO : History
    ' Consider inserting the URL in your own History
    ' container
   
    ' Prevent the default action from taking place
    Cancel = True
End Sub