Introduction
This component is trying to solve a problem I faced while I was building a
web site. At some stage of the web site building I realized I had many pages
(continually growing) with the same format and almost same content except some
parts that differed mainly in html code. The web site size was getting bigger
and bigger while only few parts of the pages where different. So I looked for a
way to keep the web site size as small as possible, while not making it hard to
manage.
The solution
The basic concept was creating an asp page that would hold the common parts
of the pages and dynamically include some other sub-page I would provide.
I did some search on MSDN and I came to the magic server side #include
statement that allows to include another sub-page in a web page. On a first
attempt I hardcoded the filename and was happy to see it working. But when I
tried to use a variable or a parameter instead of hardcoding the filename I only
received an "HTTP 500 - Internal server error".
After many failed attempts on this technique another thought was to take the
reverse path and change all the sub-pages to asp pages and use again the server
side includes to add all the other common content. I knew this would work, and
would also decrease the web site size a lot but would create another big problem
to me. First would be too much work to be done, and second would keep the
complexity of the site increasing as the pages number increased, making it
harder to manage. If I wanted later to move the sub-pages to different
subdirectories one level down categorizing them based on some keywords, I would
have to edit links to many files. The user wouldn't notice changes but the hard
part would be on me.
Another thought was to create a database, store the sub-page contents in it,
and then use a recordset to include the record text I wanted. It was easy to
create the database, add some sample pages, and test it. Althought it was
successfull, updating the page contents was hard and maintaining the site to
changes was inefficient. I had to take the record data, store it in a file, make
any changes, and store the content back to the table. It could be good it the
sub-pages where not not changing but I wanted something more flexible, quicker
and easy.
So I decided to implement something similar to server side includes on my own
and be able to:
- Have dynamic includes to keep the web site size small.
- Avoid copying of the common parts in the sub pages.
- Keep the sub pages in text/html files to benefit of the existing web
authoring tools.
- Keep all the links to only as few places as needed.
- Not require database and/or database tools to view/edit the page contents.
- Keep the maintaining process as fast and simple as possible.
- Not require learning anything new but just add a few lines to existing asp
pages.
Using the COM objects
To use this COM object you have to copy somewhere in the web server the dll
(e.g. C:\Inetpub) and register it using regsvr32 (e.g.
regsvr32 C:\Inetpub\SSDI.dll).
Then copy the files of the directory IIS Files under your web site root on
your web server. Now you are ready to test by typing
http://localhost/master.asp?file=subPageFilename on internet explorer
address bar. (subPageFilename can be test1.html or test2.html.
If no parameter provided it will default to test1.html).
Code Explanation
The asp page creates an instance of SSDI.Util
<%
...
var x=Server.CreateObject("SSDI.Util");
%>
and calls the Include member function.
<%=x.Include(Server.MapPath(szFile))%>
full asp page code:
<%@language="javascript"%>
<HTML>
<%
var szFile = String(Request.QueryString("file"));
if ((szFile == "undefined") || (szFile == ""))
szFile = "test1.html";
var x = Server.CreateObject("SSDI.Util");
%>
<BODY>
<P>We are about to include file: <%=szFile%></P>
<%=x.Include(Server.MapPath(szFile))%>
</BODY>
</HTML>
The COM object it self is a simple COM object exporting one simple interface
IUtil
with one property Include
. The code for this
property will opens the file, reads it into an allocated buffer, convert the
buffer contents to BSTR
and return it for use from scripting
languages freeing the file.
STDMETHODIMP CUtil::get_Include(BSTR filename, BSTR *pVal)
{
CString szFilename = filename;
DWORD cbRead = 0;
HANDLE hFile = ::CreateFile( szFilename, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if ( hFile == INVALID_HANDLE_VALUE )
{
pVal = NULL;
return S_FALSE;
}
DWORD cbFile = ::GetFileSize( hFile, NULL );
if ( cbFile == (DWORD) -1 )
{
CloseHandle( hFile );
return S_FALSE;
}
char *pszFileContent = (char *)LocalAlloc( LPTR, cbFile + 1 );
if ( !pszFileContent )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
CloseHandle( hFile );
return S_FALSE;
}
if ( !::ReadFile( hFile, pszFileContent, cbFile, &cbRead, NULL ))
{
CloseHandle( hFile );
LocalFree( pszFileContent );
return S_FALSE;
}
pszFileContent[cbRead] = '\0';
CString szFileContents = pszFileContent;
*pVal = szFileContents.AllocSysString();
CloseHandle( hFile );
LocalFree( pszFileContent );
return S_OK;
}
Rebuilding the demo
This demo uses (WTL 7.0) CString
to handle strings. WTL 7.0 is
available to download
from microsoft web site.