How to host IIS7 web site on Mac OSX folder via Parallels

This has bugged me for a long time. I run Parallels on my MacBook Pro because I often have to work with Windows programming technologies, mostly Visual Studio and SQL Server. When I'm developing in Windows I run a localhost copy of the web site in IIS7 (I have Vista on my Mac). Up until now I've kept the site files in the Parallels VM windows drive, but this has constantly annoyed me because I don't back up my Windows drive with Time Capsule. Take too long and causes a marked slow down in my system each hour when the backup is running. Parallels has a specific option to not back up the VM with Time Capsule and I have that selected.

Every time I've ever tried to hook an IIS web site to a folder on my Mac (eg not in the VM) it's never worked – endless errors. Finally after a bit of hunting around I pulled together various tidbits of information from the net and made it work. Here's how:

1. In Parallels configuration make sure you are using Bridged Networking, this is to ensure your VM has a separate IP number to your Mac.

2. In Mac System Preferences > Sharing, enable File Sharing. Add the root folder for your web site (on your Mac) to the list of Shared Folders and give all users all rights (I kept it simple with permissions and just gave everyone every right, it's only for my localhost so I don't think security is a big deal).

3. In Windows use File Explorer and open Network and locate your Mac and the root folder for your web site. Copy the path, on mine it looks like \\MACBOOKPRO-31F6\wwwroot

4. In Windows IIS add a new web site, In Advanced Settings set the Physical Path to the network path you copied above

5. Still in Advanced Settings set the Physical Path Credentials to your Mac user name and password. I wound up adding a new Windows user with the same user name and password as I have on the Mac. I'm not completely sure if this is the right thing to do, but heck, it worked.

6. If you already have a default web site in IIS, you'll need to sort out the bindings, you can't have more than one web site on the same port, which by default is 80. Right click the new web site and edit the Bindings. I set the port to 8080.

7. In Windows open your browser and try http://localhost:8080/, you should see the default page, if you have one, from the folder on your Mac. If, like me, it didn't work first time, try creating a simple hello.htm and calling that. I discovered my problem was simply paths and configuration variables in my web site – moral being try calling the most basic page first before blaming errors on IIS rather than your web app.

ASP: Reading multiple recordsets from a stored procedure with ASP

Reading a recordset from a stored procedure is easy, essentially we just treat the procedure statement as the SQL statement, for example, something like:

sqlStr="USP_MyProc 1,2,3"
set rs=Server.CreateObject("adodb.Recordset")
rs.cachesize=1 sqlStr, conn, adOpenForwardOnly,adLockReadOnly

But how about reading more than one recordset from a procedure? Perhaps you have a circumstance where you need a couple of resultsets within the same code block. You could have more than one procedure and open each recordset in turn (or perhaps use the same procedure and control which resultset is returned using the procedure’s parameters).

Instead why not return all the required recordists from the one procedure using the one call to the database? It can be done, a procedure can return multiple recordists and each can be read in turn from your ASP code.

The trick is NextRecordset. You may already have been using this without quite realising, for example, when inserting a new record to the database that results in a unique identity value, we usually have a need to grab that value, so we’d do something like this:

sqlStr="INSERT INTO tblMyTable (myField) VALUES (myValue);"
Set rs = conn.execute (sqlStr & "select SCOPE_IDENTITY()").nextrecordset

If you look carefully you’ll see we’re actually running two queries, first the insert, and then a second to grab the identity. You can do the same with a procedure and multiple recordists. For example, let’s say you have a procedure that returns two recordists, you just cycle through them with NextRecordset:

sqlStr="USP_MyProc 1,2,3"
set rs=Server.CreateObject("adodb.Recordset")
rs.cachesize=1 sqlStr, conn, adOpenForwardOnly,adLockReadOnly
set rs2 = rs.NextRecordSet

Now you have the first results in rs and the second batch of results in rs2.

ASP: Hiding file locations by binary stream reading

ASP: Hiding file locations by binary stream reading

I do a lot of work with internet content business – companies that earn their living by managing documents and content, either for their own services or on behalf of customers.

Often this involves making documents on the server available for viewing, but without propagating the URL of the actual file, and with permission checks to ensure that the person viewing the file is authorised to do so.

Here’s a simple technique to make a file, such as a PDF, available without revealing the file’s URL and with a security check.

The trick is to binary read the file to the user’s browser, instead of the user opening the file directly. In ASP we do this with the ADODB.Stream object. This allows us to open the file on the web server as a binary stream and send it to the browser. The user will be none the wiser to the file’s location.


' Run a security check
' Add code here to ensure that the logged in user has permission to view the file
' if we have a file identifier
if fileId>0 then
' grab the file's name and location - probably from a database table
sqlStr="SELECT filePath,fileName FROM tblMyFiles WHERE fileId="&request("fileId")
set rs=Server.CreateObject("adodb.Recordset")
rs.cachesize=1 sqlStr, conn, adOpenForwardOnly,adLockReadOnly
' if the file entry in our database table is found
if not rs.eof then
' open the file system object
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
' check the file we want does actually exist
If objFSO.FileExists(filePath&lcase(rs("fileName"))) then
' and if it does exist, then spit out to the browser
' open a binary stream for the file
Set strOutStream = Server.CreateObject( "ADODB.Stream" )
strOutStream.Type = 1 'adTypeBinary
strOutStream.Mode = 3 'adModeReadWrite
' load the file into the stream
strOutStream.LoadFromFile (filePath&lcase(rs("fileName")))
Response.Buffer = TRUE
' use the content type appropriate to the file - here we're using a PDF
Response.ContentType = "application/pdf"
Response.AddHeader "content-disposition", "inline; filename=" & rs("fileName")
Response.BinaryWrite (strOutStream.Read)
Set strOutStream = Nothing
end if
set objFSO=nothing
end if
Set rs=nothing
end if

activePDF ToolKit Error Code -998

So I don't have to yet again hunt down the activePDF ToolKit documentation, if an Error Code -998 is returned by activePDF ToolKit it means 'Product not registered/ Evaluation expired.'. And you have to take up arms and prepare to enjoy chasing down why your previously registered copy has decided, perhaps in a fit of pique, that it's happier unregistered. Which is not a fat lot of help when the bloke who has the serial number database is in a timezone slumbering through the middle of the night, it's the midst of a workday for you and you promised someone something for tomorrow.

I completely agree with software licencing. It's how companies legitimately make money. But some seem to have registration management systems calculated to drive us mere code crunchers up the wall.


Get the kids to bed! How to set access times to the internet for your children

I Tweeted the other day about having to restrict my kids' internet access to ensure they go to bed, by setting time restrictions on our wifi. I've just had another person ask, "so how do you do this?", so clearly there's a number of parents out there with the same problem. Here's how I prevent my children from accessing the internet after their bedtime.

Number one point – if you don't have an Apple WiFi network, stop reading!

We have an Apple AirPort Extreme WiFi box providing our WiFi network around the house. I'll presume you have one or more Macs – iMacs, MacBooks etc – accessing the internet via WiFi and the AirPort Extreme.

You will need to know the mac address of those computers. The mac address is nothing to do with Macintoshes, just a coincidence of naming. The mac address of a computer is a unique identifying number.

On the computer click the Apple menu (top left next to the Finder option), then click the System Preferences option. When the Preferences open click the Network icon. On the Network window you should see the AirPort connection on the left hand side. If it's not highlighted, then click it. Then click the Advanced button.


The window that opens should display the AirPort Id. Write this down.


Next open your AirPort Utility. It's in the Utilities folder in the Applications folder.


The AirPort Utility will open and your AirPort Extreme should be listed. If it's not highlighted (maybe you have more than one WiFi gadget), then click it, then click the Manual Setup button.


Your Extreme settings should load and display. Click the Access Control tab at the top right. The window will display your MAC Address Access Control page.

Set the Control to Timed Access. You should have a (default) entry called Unlimited. This means that if you don't set anything else up, everyone on your WiFi network can access the internet all the time.


Click the + button to add a new entry to the list. Enter the Mac address of the computer you want to restrict access for. Enter a description, then set the days and times as you wish. You can have more than one entry. For example, you might want to give the kids internet access until 9pm on school days, but 11pm on the weekends.


When you are finished click the Done button to close the Access window, then cick Update. The settings will be saved to the Extreme box, which will then restart, so you'll need to wait a minute or two for everything to start working again.

The only thing left is to test – which is easy. Just wait until the magic time you've designated the internet access to cease, and see how loudly your kids yell!

ASP Timeout expired error: Microsoft OLE DB Provider for SQL Server error ‘80040e31’

I was working on an ASP page today that included an AJAX call off to trigger various SQL updates, amongst which was a call to a stored procedure. The page started to error with:

Microsoft OLE DB Provider for SQL Server error ‘80040e31’
Timeout expired

The error line was the call to the stored procedure. After a bit of fiddling around I traced the issue to the ADO ConnectionTimeOut value. By default this is 30 seconds.

There are, I guess, 3 timeout values of interest on an ASP page:

1. Session timeout

How long the user’s browser session will last. On IIS this is defaulted to 20 minutes

2. Script timeout

How long a page can take to complete. On IIS this is defaulted to 90 seconds.

3. Connection timeout

How long a single call to the database can take to complete. This defaults to 30 seconds.

The stored procedure was taking > 30 seconds, hence throwing an error. I solved the problem by increasing the ConnectionTimeOut value for the particular call to the stored procedure, eg:


Dim cmdC
Set cmdC = Server.CreateObject("ADODB.Command")
with cmdC
.ActiveConnection = conn
.CommandText = "[procedure name]"
.CommandType =adCmdStoredProc
.CommandTimeout = 3000
end with


The value is in seconds, so 3000 seconds = 5 minutes. You can read more about ConnectionTimeOut here.

Of course, there’s a whole other discussion why we have a stored procedure taking so long to run as part of an AJAX call. We’ll probably shift it off to a scheduled job or something eventually but we needed a quick fix for the interim.

Displaying code in your TypePad blog

Yesterday I wanted to show some SQL code in a blog post, and realised I'd never figured out how some blogs are able to display beautifully formatted, in living colour, code in their posts.

A quick Google and all was revealed: SyntaxHighlighter from Alex Gorbatchev. I then found a handy 'how to' install SyntaxHighlighter into a TypePad blog from Damien Krotkine. Five minutes later I was up and running.