Vous êtes sur la page 1sur 906

Acrobat captured this list Saturday, December 14, 2002 - showing 430 FAQs.

Database

#2191 When should I use a recordset object (ADODB.Recordset)? (updated 04/22/2002)

#2195 What are the limitations of Access? (updated 11/08/2002)

#2201 Where can I get basic info about using stored procedures? (updated 04/15/2002)

#2009 Why do I get database-related 80004005 errors? (updated 12/10/2002)

#2149 How do I upload images to a database? (updated 08/10/2002)

#2126 How do I make DSNs without calling my ISP? (updated 09/27/2002)

#2188 How do I deal with MEMO, TEXT, HYPERLINK, and CURRENCY columns? (updated 08/23/2002)

#2193 Why does RecordCount return as -1? (updated 01/30/2002)

#2168 How do I connect to an Access database / text file on another web server? (updated 08/23/2002)

#2120 How do I page through a recordset? (updated 07/11/2002)

#2174 How do I get the ID number of a just-inserted record? (updated 07/21/2002)

#2035 How do I deal with an apostrophe (') in a SQL statement? (updated 03/06/2002)

#2096 What is wrong with 'SELECT *'? (updated 08/14/2002)

#2080 What are reserved Access, ODBC and T-SQL keywords? (updated 07/08/2002)

#2038 Which is better, rs(0) or rs("fieldname")? (updated 08/14/2000)

#2062 How do I solve 'Operation must use an updateable query' errors? (updated 08/11/2002)

#2073 Why should I avoid NULLs in my database? (updated 01/06/2001)

#2112 Should I use ADOVBS.inc for declaring constants? (updated 12/14/2001)

#2097 What is wrong with 'LIKE *'? (updated 01/23/2001)

#2132 How do I retrieve a random record? (updated 11/04/2001)

#2182 How do I upsize from Access to SQL Server? (updated 01/09/2002)

#2214 What do I need to know about the differences between Access and SQL Server? (updated 07/01/2002)

#2178 Schema: How do I get the tables out of a database? (updated 09/19/2001)

#2177 Schema: How do I get the column names (and their datatype) out of a table? (updated 06/21/2002)

#2154 Why do I get General error Unable to open registry key 'DriverId'? (updated 10/02/2002)

#2128 Why do I get 80040E10 errors? (updated 09/04/2002)

#2057 How do I know which version of MDAC I'm running? (updated 10/14/2000)

#2010 How do I hide system tables in SQL Server's Enterprise Manager? (updated 07/02/2002)

#2186 How do I connect to SQL Server on a port other than 1433? (updated 10/04/2001)

#2121 Why does ASP give me ActiveX errors when connecting to a database? (updated 08/23/2002)

#2194 How do I prevent my ASP pages from waiting for backend activity? (updated 08/14/2002)

#2037 How do I find a stored procedure containing <text>? (updated 09/06/2002)


#2260 Can I fix this mm/dd/yyyy <-> dd/mm/yyyy confusion once and for all? (updated 09/04/2002)

#2102 Why do I get 800A0BB9 / 800A1391 errors? (updated 08/28/2002)

#2150 How do I prevent NULLs in my database from mucking up my HTML? (updated 07/17/2002)

#2155 How do I enable connection pooling? (updated 06/10/2001)

#2086 Why do I get 'Syntax Error in INSERT INTO Statement' with Access? (updated 07/08/2002)

#2145 How do I debug my SQL statements? (updated 05/28/2001)

#2029 How do I create a database from ASP? (updated 07/22/2002)

#2142 Why does Access give me 'unspecified error' messages? (updated 05/23/2001)

#2159 How do I access MIN, MAX, SUM, COUNT values from SQL statements? (updated 07/08/2002)

#2082 How do I get rid of Named Pipes / DBNMPNTW errors? (updated 09/12/2002)

#2190 Can I compact / repair / synchronize an Access database from ASP code? (updated 09/05/2002)

#2130 How do I handle BIT / BOOLEAN fields in a query? (updated 05/01/2001)

#2118 How do I sort out an AND/OR query with an unknown number of parameters? (updated 04/01/2001)

#2083 How do I solve 'ADO Could Not Find The Specified Provider'? (updated 08/17/2002)

#2123 Schema: How do I get the stored procedures out of a database? (updated 04/15/2001)

#2138 I get "Login failed for user '\'." in SQL Server, why? (updated 02/07/2002)

#2152 How can I make my SQL queries case sensitive? (updated 08/22/2002)

#2105 Schema: How do I show all the triggers in a database? (updated 06/30/2002)

#2104 Schema: How do I show all the primary keys? (updated 04/30/2002)

#2165 Why does my DELETE query not work? (updated 06/26/2001)

#2160 How do I know which version of SQL Server I'm running? (updated 10/18/2002)

#2248 How do I simulate an array inside a stored procedure? (updated 03/14/2002)

#2206 Why doesn't SQL Server allow me to separate DATE and TIME? (updated 10/31/2001)

#2246 Why do I get 'BOF or EOF' errors? (updated 07/18/2002)

#2241 How do I present one-to-many relationships in my ASP page? (updated 01/23/2002)

#2041 Why do I get the error 'Command text was not set for the command object'? (updated 10/28/2001)

#2148 Why do I get weird results when using both AND and OR in a query? (updated 05/30/2001)

#2146 How do I connect to a non-default instance of SQL Server? (updated 05/28/2001)

#2164 Why do I get 80040E37 errors? (updated 08/25/2002)

#2231 Should I index my database table(s), and if so, how? (updated 01/07/2002)

#2015 How do I determine if a number is odd or even? (updated 10/28/2001)

#2197 How do I enumerate through the DSNs on a machine? (updated 08/11/2002)

#2288 What is this 'Multiple-step OLE DB operation generated errors' message? (updated 09/23/2002)

#2209 How do I document / compare my SQL Server database(s)? (updated 10/16/2002)

#2273 How do I get the number of rows in a table, or all tables? (updated 05/19/2002)

#2229 Where can I get this 'Books Online' that people keep telling me about? (updated 01/07/2002)
#2220 Why do I get SQLSetConnectAttr Failed errors? (updated 09/21/2002)

#2287 Why do I get 80040e31 / 'Timeout Expired' errors? (updated 07/18/2002)

#2259 How do I solve 'Could not find installable ISAM' errors? (updated 02/22/2002)

#2061 Why do I get 'Argument data type text is invalid for argument [...]'? (updated 09/14/2002)

#2237 Can I start IDENTITY values at a new seed? (updated 01/07/2002)

#2245 How do I time my T-SQL code? (updated 01/30/2002)

#2275 Why do I get 800A0CC1 errors? (updated 08/18/2002)

#2326 Why do I get 80040e09 errors? (updated 07/17/2002)

#2016 How do I temporarily disable a trigger? (updated 10/28/2001)

#2289 Why do I get 80040E57 / 80040E07 errors? (updated 09/14/2002)

#2243 Why does AbsolutePosition return as -1? (updated 01/30/2002)

#2423 Where else can I learn about SQL Server? (updated 11/10/2002)

#2342 How do I get the latest version of the JET OLEDB drivers? (updated 08/11/2002)

#2244 Schema: how do I retrieve the description property of a column? (updated 07/19/2002)

#2279 How do I convert columns of values into a single list? (updated 04/16/2002)

#2306 How do I limit the number of records returned in my resultset? (updated 06/27/2002)

#2345 What are the capacity specifications for Access, SQL Server, and MSDE? (updated 08/16/2002)

#2282 How do I search for special characters (e.g. %) in SQL Server? (updated 08/28/2002)

#2239 Why does Enterprise Manager crash when I get an error in a stored procedure? (updated 02/19/2002)

#2337 How do I protect my stored procedure code? (updated 07/25/2002)

#2307 Why do I get 'Operation is not allowed when the object is closed' errors? (updated 11/08/2002)

#2400 Why do I get 80040E14 errors? (updated 12/02/2002)

#2256 What does "ambiguous column name" mean? (updated 02/21/2002)

#2272 Why is Query Analyzer only returning 255 characters of my VARCHAR / TEXT column? (updated 07/22/2002)

#2319 How do I deal with multiple resultsets from a stored procedure? (updated 07/08/2002)

#2312 Why can't I use LIKE '%datepart%' queries for dates against SQL Server? (updated 07/01/2002)

#2348 Can I have optional parameters to my stored procedures? (updated 08/26/2002)

#2320 Why do I get 800a0cb3 errors? (updated 10/17/2002)

#2354 What datatype should I use for my character-based database columns? (updated 10/17/2002)

#2285 Why do I get 800A0E7D errors? (updated 09/17/2002)

#2284 Why do I get 'object could not be found' or 'invalid object name', when the object exists? (updated 04/16/2002)

#2343 What are the limitations of MSDE? (updated 11/08/2002)

#2361 Why do I get 80040E21 errors? (updated 09/17/2002)

#2426 Which database should I use for my ASP application? (updated 11/08/2002)

#2340 Why do I get 80040200 / 80040514 / 800A0E7A errors? (updated 09/27/2002)

#2314 How do I determine if a column exists in a given table? (updated 08/10/2002)

#2327 Why do I get 800a01fb errors? (updated 07/17/2002)


#2359 How do I start SQL Server Agent from ASP? (updated 08/14/2002)

#2352 How do I handle alphabetic pagination? (updated 08/10/2002)

#2351 How do I determine if a database exists? (updated 08/10/2002)

#2350 How do I determine if a table exists in the database? (updated 08/10/2002)

#2292 How do I solve 'Cannot open a database created with a previous version...' errors? (updated 04/30/2002)

#2394 Can I use the NZ() function without getting 80040E14 errors? (updated 08/26/2002)

#2332 Why do I get 80040e4e errors? (updated 07/18/2002)

#2301 Why do I get 'Not enough space on temporary disk' errors? (updated 07/10/2002)

#2293 Why do I get script errors in Enterprise Manager's 'taskpad' view? (updated 05/02/2002)

#2328 Why do I get 80040e30 errors? (updated 07/17/2002)

#2367 Why do I get 800A0C93 errors? (updated 08/17/2002)

#2381 Why do I get 80040E24 errors? (updated 08/19/2002)

#2370 Why do I get 80040E2F errors? (updated 08/18/2002)

#2417 Why do I get 80040E54 errors? (updated 09/20/2002)

#2409 Why do I get 80070070 errors? (updated 09/15/2002)

#2406 Why do I get 8002000A errors? (updated 09/15/2002)

#2427 How do I return row numbers with my query? (updated 12/09/2002)

#2428 How do I get a list of tables and their record counts? (updated 12/10/2002)

Components

#2053 How do I store objects or components in session/application variables? (updated 09/15/2002)

#2087 DLL: How do I avoid 'Permission Denied' when re-compiling? (updated 10/28/2001)

#2031 When does ASP release COM objects? (updated 08/13/2000)

#2184 How do I detect browsers without components? (updated 12/04/2002)

#2207 How do I generate PDF files from ASP? (updated 10/18/2002)

#2135 How do I determine if a COM object is installed? (updated 05/08/2001)

#2185 Why does Browscap give me 'unknown' or tell me IE is Netscape? (updated 12/04/2002)

#2091 Why do I get 'Server.CreateObject Access Error'? (updated 01/22/2001)

#2131 Why do I get 8007000E errors? (updated 11/08/2002)

#2134 Why do I get 800401F3 / 800A01AD errors? (updated 09/14/2002)

#2225 Where can I get a shopping cart for my web site? (updated 04/11/2002)

#2199 Where can I get an updated Browscap.ini? (updated 12/04/2002)

#2203 How do I pass server-side values to a client-side ActiveX control? (updated 10/30/2001)

#2216 Can I code ISAPI filters / extensions with Visual Basic? (updated 07/25/2002)

#2336 Should I instantiate my COM object with CreateObject or Server.CreateObject? (updated 07/24/2002)
#2395 How do I generate RTF documents from ASP? (updated 08/27/2002)

#2322 Why do I get 8000401A errors? (updated 09/15/2002)

#2396 How do I determine if an object exists? (updated 08/29/2002)

#2393 Why do I get 80040111 errors? (updated 08/25/2002)

#2397 How do I handle MD5 from ASP? (updated 09/04/2002)

#2362 Why do I get 8007007E errors? (updated 09/14/2002)

#2388 Why do I get 80040112 / 8007045A errors? (updated 10/02/2002)

#2385 Why do I get 800A005B errors? (updated 08/19/2002)

#2391 Why do I get 80072EE5 errors? (updated 08/25/2002)

#2341 Why do I get 80040460 errors? (updated 08/07/2002)

#2407 Why do I get 80072EE2 errors? (updated 09/15/2002)

#2357 Why do I get 800A0030 errors? (updated 08/14/2002)

#2411 Why do I get 80040514 errors? (updated 09/15/2002)

#2410 Why do I get 800C0007 errors? (updated 09/15/2002)

Forms

#2189 How do I upload files from the client to the server? (updated 04/03/2002)

#2052 How do I change the target frame or window of a response.redirect? (updated 07/16/2002)

#2028 How do I validate a credit card number in ASP? (updated 07/27/2000)

#2153 How can I mimic a client-side POST from ASP? (updated 06/08/2001)

#2036 How do I iterate through a form collection? (updated 10/28/2001)

#2106 How do I validate forms using server side script? (updated 02/25/2001)

#2111 Which is faster: Request("item") or Request.Form("item")? (updated 09/16/2002)

#2019 Why does my input type=text value get truncated? (updated 09/10/2002)

#2008 How do I retrieve the name of the form that was submitted? (updated 07/09/2000)

#2020 Why does my form variable become 'value, value' instead of 'value'? (updated 07/11/2000)

#2077 What is the size limit of a posted FORM field? (updated 02/21/2002)

#2223 What is the limit on Form / POST parameters? (updated 02/21/2002)

#2116 How do I cause/prevent ENTER being used to submit a form? (updated 03/26/2001)

#2270 How do I make one dropdown depend on another? (updated 06/17/2002)

#2222 What is the limit on QueryString / GET / URL parameters? (updated 11/13/2001)

#2166 When I'm uploading files, why can't I access the request.form collection? (updated 03/07/2002)

#2215 How do I perform spell checking from a web page? (updated 12/09/2002)

#2242 When I have multiple submit buttons, how do I tell which was clicked? (updated 01/25/2002)

#2213 How do I disable certain FORM elements? (updated 11/01/2001)

#2264 How do I retrieve the text and the value from a <SELECT> element? (updated 04/15/2002)
#2234 How do I make form fields read-only? (updated 01/07/2002)

#2255 Why won't my <TEXTAREA> display the data I passed to it from ASP? (updated 02/20/2002)

#2230 How do I disable IE's Autocomplete feature? (updated 06/27/2002)

#2235 How can I programmatically interfere with the INPUT TYPE=FILE element? (updated 01/07/2002)

#2204 How do I pass x-y coordinates to ASP, after the user clicks an image? (updated 10/30/2001)

#2353 How do I submit a form to a new window, with the control of window.open()? (updated 08/11/2002)

#2253 Why do I get 'HTTP 405 - Resource Not Allowed' errors? (updated 08/19/2002)

Filesystem

#2042 How do I dynamically include files? (updated 08/10/2002)

#2039 Where can I find info on working with files and FileSystemObject? (updated 08/15/2000)

#2170 How do I create / manipulate images from ASP? (updated 09/24/2002)

#2025 How do I sort a list of files? (updated 06/30/2002)

#2129 How do I make the filename correct for the client, when using binaryWrite? (updated 03/10/2002)

#2180 Why does FileSystemObject hang all of a sudden? (updated 07/02/2002)

#2072 How do I get the name of the current URL / page? (updated 08/15/2002)

#2090 Why do I get 'Permission Denied' errors with FileSystemObject? (updated 01/20/2001)

#2074 Can I rename a file using FileSystemObject? (updated 03/08/2002)

#2089 Why do I get 'Path not found' errors with FileSystemObject? (updated 06/14/2002)

#2276 How do I prevent people from 'leeching' my images? (updated 06/30/2002)

#2236 Can I place a file on a user's hard drive without bothering them with a prompt? (updated 01/07/2002)

#2211 How do I get a list of a folder's subfolders? (updated 11/01/2001)

#2205 Why do I get permissions errors after upgrading to Windows XP? (updated 02/04/2002)

#2208 Can I include a file in both server-side script and client-side script? (updated 01/30/2002)

#2101 Why do I get 'Invalid procedure call or argument'? (updated 08/27/2002)

#2088 Why do I get 'Disk not ready' errors with FileSystemObject? (updated 01/20/2001)

#2277 How do I prevent people from 'leeching' my CSS or JS files? (updated 04/15/2002)

#2278 How do I prevent that ugly red x when an image is missing from my server? (updated 04/15/2002)

#2296 How do I determine the owner of a file? (updated 06/11/2002)

#2425 How do I use FileSystemObject to create a file on the client? (updated 10/17/2002)

#2302 Why do I get an 'Invalid Path Character' error? (updated 09/21/2002)

#2303 How do I change the modified time of a file? (updated 06/25/2002)

#2316 Why is 'the operation completed successfully' an error message? (updated 08/19/2002)

#2379 Why do I get 800A0034 errors? (updated 08/19/2002)

#2387 Why do I get 800A0BBA errors? (updated 08/19/2002)


#2429 How do I retrieve a random file? (updated 12/10/2002)

Email

#2119 How do I send e-mail from ASP? (updated 09/11/2002)

#2026 How do I send e-mail with CDONTS? (updated 11/18/2002)

#2032 How do I put carriage returns into an e-mail? (updated 08/13/2000)

#2238 How do I validate an e-mail address? (updated 01/08/2002)

#2268 Why does my CDONTS mail hang out in the queue or pickup folders? (updated 07/19/2002)

#2291 Why can't ASP handle 80,000 e-mails? (updated 11/22/2002)

#2252 Can I get CDO messages to return a read receipt? (updated 11/18/2002)

#2254 Why do CDONTS messages end up in the badmail folder? (updated 07/19/2002)

#2295 How do I prevent my links from wrapping in an e-mail? (updated 09/09/2002)

#2403 How do I send e-mail from SQL Server? (updated 12/02/2002)

#2308 Where can I get more information about configuring and using CDO / CDONTS? (updated 07/19/2002)

#2339 Can I use a remote SMTP server with CDONTS.NewMail? (updated 07/30/2002)

#2305 Why does CDO.Message give me an 8004020f error? (updated 06/27/2002)

#2372 Why do I get C00402CE / C00402C7 errors? (updated 08/19/2002)

#2315 How do I alter the priority of a CDO message? (updated 07/08/2002)

#2377 Why do I get 80090020 errors? (updated 08/19/2002)

#2418 Why does CDO.Message give me 80040222 errors? (updated 09/21/2002)

#2386 Why do I get 80040108 errors? (updated 08/19/2002)

#2378 Why do I get 8004020A errors? (updated 08/19/2002)

#2365 Why do I get 8000900F errors? (updated 08/17/2002)

#2405 Why does CDO.Message give me 80040213 errors? (updated 09/13/2002)

Dates

#2040 Could I get a little help with dates? (updated 09/17/2002)

#2023 How do I delimit dates for inserting/updating a database? (updated 02/25/2002)

#2049 SQL Server: How do I select time only from a DATETIME field? (updated 09/17/2000)

#2024 Why does JavaScript's document.lastModified() not work in ASP files? (updated 10/28/2001)

#2093 Can I get millisecond accuracy in ASP? (updated 02/25/2002)

#2192 How do I display time in military format? (updated 10/21/2001)

#2233 Given two dates, how do I determine an age? (updated 08/26/2002)

#2218 How do I convert local time to UTC (GMT) time? (updated 11/12/2001)

#2313 How do I format a date in ways not offered by FormatDateTime? (updated 07/22/2002)
#2280 Should I use 'BETWEEN' for date queries? (updated 04/15/2002)

#2309 How do I implement a calendar / datepicker in ASP? (updated 07/02/2002)

#2219 How do I determine the number of seconds since 1/1/1970? (updated 11/12/2001)

#2271 How do I convert a timespan, in seconds, to HH:MM:SS? (updated 03/27/2002)

#2347 Why do I have problems inserting NOW() into a database? (updated 08/10/2002)

General

#2022 How do I prevent my ASP pages from caching? (updated 11/07/2002)

#2059 How do I execute a DOS command / batch file / exe from ASP? (updated 08/27/2002)

#2173 How do I read the contents of a remote web page? (updated 09/21/2002)

#2171 What is this 'ASP 0115' error? (updated 09/16/2002)

#2183 Where else can I learn about ASP? (updated 07/12/2002)

#2017 How do I disable the back/forward buttons? (updated 07/11/2000)

#2014 How do I control printing from ASP? (updated 08/27/2002)

#2043 How do I get screen resolution from ASP? (updated 03/06/2002)

#2143 How do I schedule ASP files? (updated 09/09/2002)

#2109 Why do I get a 500 Internal Server error for all ASP errors? (updated 08/12/2002)

#2078 What is wrong with Session_OnEnd()? (updated 10/28/2001)

#2055 How do I use ASP to [...] (updated 10/13/2000)

#2114 How do I control access to an area? (updated 11/13/2001)

#2081 How do I make sure my ASP question gets answered? (updated 01/13/2001)

#2001 How do I make JavaScript send values to ASP? (updated 06/14/2002)

#2012 How do I get the user's IP address or browser information? (updated 07/30/2002)

#2163 How do I protect my ASP code? (updated 06/26/2001)

#2075 Where do I get ASP? (updated 10/30/2001)

#2172 How do I manage a session across multiple windows? (updated 11/12/2001)

#2162 How can I give them a better 404 message? (updated 04/15/2002)

#2011 Why do I get an HTTP Header or Object Moved error when trying to redirect? (updated 09/11/2002)

#2058 How do I detect ENABLED cookies / javascript? (updated 07/02/2002)

#2181 How do I run ASP on other web servers besides IIS? (updated 09/12/2002)

#2045 Does order matter when using <%%>, <script runat=server>, and different languages? (updated 09/04/2000)

#2071 What's the deal with IIS 5.0 and ASP 3.0? (updated 12/13/2000)

#2002 How do I make sure people go to page x before page y? (updated 07/09/2000)

#2046 How do I get the login name / username from the person visiting my page? (updated 07/26/2002)

#2136 Why do I get script errors on one machine but not another? (updated 12/12/2002)
#2064 Where can I host ASP pages for free (or at least cheap)? (updated 11/18/2002)

#2167 How do I get IntelliSense to see ASP 3.0 methods? (updated 10/31/2001)

#2169 What is wrong with Request.ServerVariables("HTTP_REFERER")? (updated 11/08/2002)

#2084 When I run a page in my browser, why does the ASP code not execute? (updated 09/05/2002)

#2251 How can I track when my site is added to a user's favorites? (updated 04/16/2002)

#2003 How can I stop Photoshop from opening ASP files? (updated 07/09/2000)

#2004 Why does my page render (properly) in IE and not in Netscape? (updated 07/09/2000)

#2066 How do I increase timeout values? (updated 09/15/2002)

#2044 Why does REMOTE_HOST return an IP address instead of a name address? (updated 07/29/2002)

#2161 How do I prompt a "Save As" dialog for an accepted mime type? (updated 07/08/2002)

#2006 Why am I having problems with Server.Execute and/or Server.Transfer? (updated 09/06/2002)

#2079 Can I run IIS on Windows Millennium or Windows XP Home? (updated 12/06/2002)

#2113 Why is Netscape slow in IIS 5.0? (updated 03/16/2001)

#2217 What do I need to know about Response.Redirect? (updated 11/06/2001)

#2147 Why does IIS 5.0 stop serving ASP pages? (updated 10/28/2001)

#2013 Can I detect plug-ins/ActiveX controls from ASP? (updated 07/10/2000)

#2141 How do I make my ASP page pause or 'sleep'? (updated 06/21/2002)

#2027 How do I make Visual InterDev's debugging features work? (updated 01/30/2002)

#2076 Why does global.asa not fire? (updated 01/09/2001)

#2050 How do I fix the ::$DATA bug? (updated 09/18/2000)

#2021 How do I access my server's registry from an ASP page? (updated 10/28/2001)

#2176 Should I use VBScript or JScript for ASP? (updated 02/04/2002)

#2157 Why won't my session variables stick? (updated 09/07/2002)

#2054 How do I persist a shopping cart without session variables? (updated 10/01/2000)

#2069 Can I use IP address to uniquely identify visitors? (updated 12/10/2000)

#2065 How do I embed apostrophes (') and quotes (") in a string? (updated 12/10/2000)

#2187 How do I zip / unzip files from ASP? (updated 10/04/2001)

#2070 Why can't I pass querystring information AND links to #bookmarks? (updated 12/13/2000)

#2047 Why can't I use #EXEC in an ASP page? (updated 09/14/2000)

#2098 Why do I get 'Name redefined' errors? (updated 06/30/2002)

#2067 Can I create an array's size dynamically? (updated 12/02/2002)

#2092 How do I time my ASP code? (updated 01/23/2001)

#2005 Why can't I browse localhost without a connection? (updated 07/09/2000)

#2033 How do I execute a ping command from ASP, and retrieve the results? (updated 08/27/2002)

#2158 How do I make my ASP page refresh? (updated 07/08/2002)

#2060 How do I use extensions other than .ASP for ASP files? (updated 10/26/2000)

#2139 How do I stress test my ASP application? (updated 11/05/2002)


#2110 How do I FTP files from ASP? (updated 06/12/2002)

#2007 Why do I get an 'overflow' error using CInt? (updated 07/09/2000)

#2198 How do I send a MsgBox or InputBox from ASP? (updated 09/20/2002)

#2103 Where can I find out about .NET? (updated 01/07/2002)

#2144 How do I refresh global.asa without restarting the application? (updated 05/27/2001)

#2175 How do I protect my client-side JavaScript code? (updated 10/30/2001)

#2095 How do I access all active sessions on the server? (updated 01/23/2001)

#2099 What is this error 'An unhandled data type was encountered'? (updated 08/11/2002)

#2085 Should I use sessionID to uniquely identify users (e.g. primary key)? (updated 01/20/2001)

#2030 Why won't QueryString values work with Server.Execute? (updated 08/06/2000)

#2034 Can I run IIS 5.0 / ASP 3.0 on Windows NT 4.0 or Windows 9x? (updated 10/30/2001)

#2227 Why does DLLHOST.EXE take all my memory and/or CPU? (updated 08/20/2002)

#2108 How do I generate unique GUIDs from ASP? (updated 10/28/2001)

#2051 What is this 'Cannot detect OS type' error with NT 4.0 Option Pack? (updated 09/18/2000)

#2125 How do I solve 'The server failed to load the application' errors? (updated 04/17/2001)

#2056 Can I host multiple sites in Workstation or Professional? (updated 10/30/2001)

#2068 How do I get the lbound/ubound of the nth element in a multi-dimensional array? (updated 12/10/2000)

#2151 Is there an easier way to patch my server(s)? (updated 12/14/2002)

#2224 What kind of object is Response.Crackers? (updated 11/13/2001)

#2100 How do I embed ASP delimiters (<% or %>) in a string? (updated 09/04/2002)

#2094 Can I dictate the load order of files on the client from ASP? (updated 01/23/2001)

#2179 How do I comment blocks of ASP code? (updated 06/30/2002)

#2018 Where can I find out about running Perl in IIS? (updated 07/11/2000)

#2265 How do I warn people when their session is about to expire? (updated 03/10/2002)

#2140 How do I determine which version of IIS / ASP I'm running? (updated 05/20/2001)

#2200 How do I turn a KB Article #, like Q191987, into a usable URL? (updated 12/02/2002)

#2232 How do I make the search engines index my ASP pages with QueryStrings? (updated 07/09/2002)

#2107 How do I host multiple web sites on one IIS box? (updated 02/26/2001)

#2281 How do I set session variables from client-side script? (updated 06/14/2002)

#2115 Why do I get 800A0414 errors? (updated 08/19/2002)

#2117 How do I round a number *properly* with VBScript? (updated 04/01/2001)

#2133 How do I know which version of VBScript my server is running? (updated 05/07/2001)

#2424 How do I make my ASP pages more efficient? (updated 11/25/2002)

#2226 What is Event ID 36, and how can I get IIS running again? (updated 09/15/2002)

#2269 Should I use the .inc extension for my include files? (updated 03/13/2002)

#2249 Can I perform simple encryption / decryption in ASP? (updated 02/06/2002)


#2124 How do I change document names / extensions in IIS / PWS? (updated 10/29/2001)

#2156 How do I detect the browser's encryption level / cipher strength? (updated 06/10/2001)

#2048 Why do I get 'HTTP/1.0 Invalid Application Name' errors? (updated 09/14/2000)

#2127 What do I do when IIS 5.0 will not start? (updated 04/25/2001)

#2196 I have plenty of RAM, why do I get an 'Out of memory' error? (updated 09/11/2002)

#2413 Why do I get non-database-related 80004005 errors? (updated 09/21/2002)

#2137 How do I solve 'The specified procedure could not be found' errors? (updated 05/10/2001)

#2228 How do I display the Euro symbol ( ) in my ASP pages? (updated 03/22/2002)

#2335 How do I log / track ASP errors on my web site? (updated 08/06/2002)

#2122 Why do I get an error about a 'Smart HTML interpreter'? (updated 04/11/2001)

#2221 Why do I get 'Type Mismatch' when using the Session object? (updated 07/08/2002)

#2263 How do I get the computer name / IP address of the server? (updated 09/25/2002)

#2266 Why do I get 'HTTP 500-12 Application Restarting' errors? (updated 08/16/2002)

#2240 How do I protect my images and other visual content? (updated 06/30/2002)

#2257 Why are people telling me to fix my clock / timezone? (updated 02/21/2002)

#2355 How do I solve 'Event ID 5' errors? (updated 09/27/2002)

#2247 How do I change a list into a set of table rows and columns? (updated 06/14/2002)

#2212 How do I convert exchange rates in ASP? (updated 11/01/2001)

#2286 Why does session.abandon not take effect right away? (updated 04/16/2002)

#2261 Why can't I retrieve custom header information from Request.ServerVariables()? (updated 02/25/2002)

#2063 How do I embed a TAB character into source code? (updated 07/08/2002)

#2267 Why do I get 'The RPC Server is Unavailable' messages? (updated 03/04/2002)

#2274 How do I convert numbers into words? (updated 04/15/2002)

#2311 How do I generate a treeview from ASP? (updated 06/30/2002)

#2283 Why do I get the error Object Required: ''? (updated 09/06/2002)

#2202 How can I increase the amount of connections in Workstation / Professional? (updated 10/30/2001)

#2389 Why do I get errors in the 800A0001 -> 800A000F range? (updated 09/17/2002)

#2262 Why can't I turn buffering off using Response.Buffer in IIS 5.0 and up? (updated 10/17/2002)

#2250 How do I find out the amount of space left on my server? (updated 02/20/2002)

#2290 How do I make sure the client is still connected before processing? (updated 04/22/2002)

#2298 How do I perform a Whois / DNS lookup from ASP? (updated 10/30/2002)

#2210 How do I change the default server scripting language in InterDev? (updated 11/01/2001)

#2300 How do I pad digits with leading zeros? (updated 09/17/2002)

#2297 How do I deal with disappearing application variables? (updated 08/15/2002)

#2323 Why do I get 8000FFFF / 8002802B errors? (updated 09/29/2002)

#2321 How do I redirect a user to https:// if they access a page with only http://? (updated 07/11/2002)

#2390 Why does IsNumeric() return true for some strings that contain characters? (updated 11/26/2002)
#2330 Why do I get 80020005 errors? (updated 08/12/2002)

#2344 How do I highlight words in a string? (updated 08/09/2002)

#2404 How do I decode an encoded URL? (updated 09/10/2002)

#2369 How do I make hyperlinks out of plain text URLs and e-mail addresses? (updated 08/19/2002)

#2421 Why do I get 80020009 errors? (updated 09/27/2002)

#2346 What could cause all of my session or application variables to disappear? (updated 08/15/2002)

#2299 How do I convert a name to proper case? (updated 06/18/2002)

#2258 Why am I having problems installing Visual Studio.NET RTM? (updated 02/21/2002)

#2310 Why do I get 'Object doesn't support this property or method' errors? (updated 08/19/2002)

#2333 Can I mimic VBScript's trim, ltrim, rtrim in server-side JScript / JavaScript? (updated 07/19/2002)

#2317 Why am I getting 'subscript out of range' errors? (updated 07/08/2002)

#2294 Why do I get 'A script block cannot be placed inside another script block' errors? (updated 05/15/2002)

#2334 How do I get the server's timezone information? (updated 07/21/2002)

#2304 How do I prevent 'Invalid use of Null' errors? (updated 06/27/2002)

#2349 Can I have optional parameters to my subs / functions? (updated 08/10/2002)

#2356 How do I solve 'The Requested Resource is in Use' errors? (updated 08/12/2002)

#2366 Why do I get ASP 0113 / Script timed out errors? (updated 08/17/2002)

#2325 Why do I get 80010108 errors? (updated 08/21/2002)

#2399 How do I make sure an entered string contains only valid characters? (updated 09/04/2002)

#2338 How do I convert from Hex to Decimal and back? (updated 07/29/2002)

#2398 How do I print the first n characters of a large block of text? (updated 09/04/2002)

#2318 Why do I get 80090016 errors? (updated 07/08/2002)

#2414 Why do I get errors in the 800A0400 -> 800A0415 range? (updated 09/16/2002)

#2401 How do I parse the domain name out of a URL? (updated 09/09/2002)

#2383 Why do I get ASP 0101 errors? (updated 08/20/2002)

#2360 Why do I get 80070057 errors? (updated 08/14/2002)

#2358 Why does GUID not work correctly with response.write? (updated 08/14/2002)

#2402 How do I parse the file name out of a path or URL? (updated 09/09/2002)

#2415 How do I make sure my servers have the same time? (updated 09/16/2002)

#2363 Why do I get 800A0401 errors? (updated 08/16/2002)

#2329 How do I convert old IDC / HTX pages to ASP? (updated 07/17/2002)

#2331 Why do I get 80029c84 errors? (updated 07/18/2002)

#2392 Why do I get 80070034 / 80070035 errors? (updated 08/25/2002)

#2384 Why do I get 8002000E errors? (updated 08/19/2002)

#2371 Why do I get 'Invalid Default Script Language' errors? (updated 08/18/2002)

#2364 Why do I get 800A01F4 errors? (updated 08/17/2002)


#2324 Why do I get 80020003 errors? (updated 11/08/2002)

#2422 Why do I get errors in the 800A0030 -> 800A003A range? (updated 09/23/2002)

#2376 Why do I get 800A0408 errors? (updated 08/19/2002)

#2412 Why do I get ASP 0130 / ASP 0131 errors? (updated 09/15/2002)

#2416 Why do I get 800A03EC errors? (updated 09/20/2002)

#2368 Why do I get 800A03F6 errors? (updated 08/17/2002)

#2373 Why do I get 800A01C2 errors? (updated 09/04/2002)

#2382 Why do I get 800A138F errors? (updated 08/19/2002)

#2419 Why do I get 80010105 errors? (updated 09/21/2002)

#2375 Why do I get ASP 0158 errors? (updated 08/19/2002)

#2408 Why do I get 80070056 errors? (updated 09/15/2002)

#2420 Why do I get 800A01F9 errors? (updated 09/21/2002)

#2380 Why do I get 8004E00F errors? (updated 08/19/2002)

#2374 Why do I get 800A01CA errors? (updated 08/19/2002)

#2430 How do I count the number of times x occurs in string y? (updated 12/14/2002)
When should I use a recordset object (ADODB.Recordset)?

38,552 requests - last updated Monday, April 22, 2002

There has been much discussion about when a person should use the various objects associated with ADO. What

follows are some loose guidelines for the correct usage of these objects. This article is by no means the total picture,

but rather will attempt to give the programmer the information to decide which method is 'right' for the purpose they

intend to use data access.

A quick note: NEVER store a recordset in the session object. For a discussion of this see:

http://www.microsoft.com/mind/1198/ado/ado.htm (tip #6)

Q176056

Most data access tasks can be implemented by using the execute method of the connection object. Why would we

want to do this? Well, for one, there is extra overhead in using a recordset object for UPDATE and INSERT

functionality. This is because the provider has to translate your code into an equivalent T-SQL statement anyway

(the database itself has no knowledge of "addNew" and similar methods). Also, there are many dangerous locks

associated with recordsets... most of which are not necessary (especially when performing an INSERT or UPDATE).

Your goal should be to get in, tweak your data, and get out as quickly as possible. Using direct statements is the

quickest way to do this, since there is much less overhead and no locks associated with your activity.

Another benefit of using an INSERT or UPDATE statement is that it is much easier to debug. You can change

conn.execute(sql) to response.write(sql) and immediately see why your statement is throwing an error. With a multi-

line transaction using a recordset object, it is translated to an INSERT or UPDATE statement (inefficient!) on the DB

side, so there is no straightforward way to trap errors at the code level.

To use the connection object, simply design a transact-SQL statement for the action you want to use and implement

it like so:
<%
sql = "INSERT INTO <table> (fields) VALUES (values)"
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
conn.execute sql, , &H00000080
...
%>

No recordset object is needed for this, because there is no need to return data (and in this case, your performance

will increase if you add '&H00000080' as the third parameter - the constant for adExecuteNoRecords). If there were

a need for returned data in the form of a recordset, we would do it like so:

<%
sql = "SELECT field1, field2 FROM <table>"
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute(sql)
...
%>

The command object is also unnecessary overhead that does little, aside from bloat code, create confusion, and

require constant declaration (ADOVBS.INC = bad). Even for executing stored procedures, you can do it with the

connection object alone:

<%
sql = "EXEC SP_doSomething @param1=1, @param2='" & var & "'"
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute(sql)
...
%>

Even a recordcount can be obtained without having to create an entire instance of an object (see Article #2193).

There are a few exceptions to this rule, of course. When certain ADO methods need to be used, or the cursorType
needs to be changed for any other reason (e.g. for paging through a resultset), it may be necessary to use a

recordset object.
What are the limitations of Access?

38,225 requests - last updated Friday, November 8, 2002

It's no secret: I hate Access. I don't think it should be used in a production environment for anything more than a

personal website, and an unpopular one at that. If you're building a web site that you expect will be even remotely

successful, you're only delaying the inevitable by using Access now. You will eventually be forced to upgrade to SQL

Server (or something similar).

I have many reasons for this, most from first- or second-hand experience. I'm not going to get into all of those right

now, because it is somewhat biased by my experience, and the type of applications I specialize in (which likely

varies from the typical ASP programmer).

My primary reason, however, is that Access (and the Jet drivers) can only manage a handful of users at any given

time (see Q154869 and this interesting discussion for some background on this). I've received plenty of criticism

from people who expect there to be a hard-lined, cover-all-your-bases, magic number for how many users an Access

database can support. Sorry to disappoint you, but there is no such magic number. There are far too many variables

involved, such as (in no particular order):

■ ODBC driver version

■ database structure

■ number of tables queried

■ number of queries per user

■ size of tables

■ number of records

■ number of fields

■ size of fields

■ use of stored queries

■ use / size of indices

■ bandwidth

■ processor speed

■ disk subsystem

■ RAM

■ hard drive space

■ behavior of users
Microsoft doesn't even publish hard numbers; they just use off-hand references such as "ten or fewer" and never

truly define what they mean by "high-concurrency."

For some people, a handful of concurrent users is fine. If you have a site with a guestbook, and you get a few dozen

entries a week, Access should be sufficient. But if you have a site with database-driven navigation, full search

functionality and megs of data flowing each day, you may want to read this article in its entirety before settling on

Access.

======================

From Q222135

"While Microsoft Jet is consciously (and continually) updated with many quality, functional, and performance

improvements, it was not intended (or architected) for the high-stress performance required by 24x7 scenarios,

ACID transactions, or unlimited users, that is, scenarios where there has to be absolute data integrity or very high

concurrency."

"ASP also supports using the Microsoft Jet database engine as a valid data source. The Access ODBC Driver and

Microsoft OLE DB Provider for Microsoft Jet are not intended to be used with high-stress, high-concurrency, 24x7

server applications, such as web, commerce, transactional, messaging servers, and so on."

======================

From Q225048

"First, Microsoft Jet can only handle a limited number of sessions. If your application uses a large number of ADO

Data controls, Jet may run out of resources."

======================

From Q240317

"Microsoft Jet has a read-cache that is updated every PageTimeout milliseconds (default is 5000ms = 5 seconds). It

also has a lazy-write mechanism that operates on a separate thread to main processing and thus writes changes to

disk asynchronously. These two mechanisms help boost performance, but in certain situations that require high

concurrency, they may create problems."


======================

From http://msdn.microsoft.com/library/en-us/dnmsde/html/msdeforvs.asp

"Jet can support up to 255 concurrent users, but performance of the file-based architecture can prevent its use for

many concurrent users. In general, it is best to use Jet for 10 or fewer concurrent users."

======================

From http://msdn.microsoft.com/library/en-us/dnduwam/html/d2dbase.asp

"Phase 2 of the Duwamish Books sample includes a Microsoft® Access .mdb database, although this technology

supports fewer concurrent users than does Microsoft SQL Server."

======================

If you're going to try Access, you'll need to test your application under stress and heavy load. I would max out at

two to three times your expected volume (because your volume will have peaks and valleys). See Article #2319 for

a list of tools and services designed to stress test your web site(s).

You're not going to get hard numbers from anyone else that apply specifically to your application. It is YOUR

responsibility to benchmark YOUR application and make sure that Access can handle the maximum number of

simultaneous users YOU expect (or hope for). Not performing this kind of test is doing a disservice to you, your

employer, and your client -- and opening all up to risk of failure.

With all that aside, if you're stuck with Access, here is a list of links to ASPFAQ and KB articles revolving around

specific Access error messages. This isn't a completely exhaustive list, but it should serve to be a good starting

point.

80040e07 - Data type mismatch in criteria expression.

Article #2289, Q136059, Q175258, Q184948, Q208425, Q210244, Q246570

80040e10 - Too few parameters. Expected <x>.

Article #2128, Q162980, Q171850, Q172898, Q178070, Q181209, Q181832, Q184233, Q191619, Q205972,
Q207586, Q216425, Q237994

80040e14 / 80040e37 - The Microsoft Jet Database engine cannot find the table or query 'tablename'. Make sure it

exists and that its name is spelled correctly.

Article #2164, Q184572

80040e14 - Syntax error in <statement or clause>.

Article #2086, Q181489

80040e4d - Too many client tasks.

Q154869

80004005 - *

Article #2009

==================

Aside from the concurrent user and permissions problems, Access lacks many other qualities of a mission-critical,

enterprise-level database.

==================

Scalability

==================

I don't believe it can be stressed enough that Access will simply not stand up to traffic. I realize it is tough to

simulate TRUE load testing in a development environment, but if it can prevent you from launching an inadequate

database, it will be worth the trouble. Mission-critical databases should laugh at traffic; database servers should

buckle under bandwidth and RAM contsraints before they're ever stopped by the database itself. As an added point,

SQL Server has the flexibility of actually using multiple processes on an SMP machine.

See Article #2345 for a comparison of the capacities of Access, SQL Server and MSDE. If you can deal with a 2 GB

size limitation and a built-in limitation of 5 concurrent workloads, you will be much better off starting with MSDE

than with Access - especially if you

==================

Integrity
==================

From Q300216:

"Unlike a file-based database engine, a server-based database engine such as Microsoft SQL Server processes all of

the multiple client requests to a database at the server. The server keeps track of these requests in a transaction

log. If, for any reason, a request cannot be fulfilled, the server rolls back or does not process the request. This

reduces the possibility that the database will be left in an incomplete or corrupted state."

Access doesn't have a good backup scheme; in fact, it doesn't have a backup scheme at all. Being file-based, there

are two problems with attempting to back up an Access database: (1) if records are modified while the backup is

being performed, the backup may become corrupt; and, (2) many backup programs won't even touch a file that is in

use. Most capable database systems have a variety of configurable backup schemes, and SQL Server is no

exception. SQL Server also has comprehensive locking facilities, making it much more difficult to corrupt a backup.

Similarly, it is near impossible to modify an Access database while it is 'live', e.g. while any user has a page opened

that is accessing any table within, or if another user has the MDB file open in the Access GUI. You have to copy the

database, make your changes, and replace the 'live' version - waiting until nobody is on your site. Not something

that can be tolerated in a 24/7 environment.

==================

Replication

==================

A decent database system has at least one way to replicate / transfer content from one database, or one server, to

another. SQL Server has multiple options for Data Transformation Services (DTS), and can have external tools

"plugged in" to perform similar tasks. With versions of Access prior to 2000, it was always right-click, copy .mdb file,

paste. Yuck.

==================

Security

==================

Capable database platforms have multiple levels of configurable security, down to the object level. A user can also

be permitted across databases and across servers. Through the context of ASP, Access only has the ability to
password protect a database on a single file basis, so you can't have custom permissions per query, table or view.

Another element of security is that even password-protected MDB files are easily compromised simply by importing

them into an Access 2000 application.

==================

Transactions

==================

SQL Server is a transactional database. Aside from remote stored procedures, any set of operations within a

transaction can be rolled back. With Access, you would have to either (a) use transactions from an external

application, e.g. COM+ or MTS; or (b) revert to a previous copy of the database.

==================

Triggers

==================

In SQL Server, triggers allow you to perform operations in response to certain events without slowing down the

calling application. For example, you could have SQL Mail send you an e-mail after every five inserts to the ORDERS

table where the order total is greater than $50. With Access, you would have to create a table, store a count for the

number of times such an insert occurs, and code the application to send mail at insert time (which slows down the

application itself). Not the prettiest solution.

==================

SQL Mail

==================

SQL Server supports a native mail format; as long as there is an Exchange Server within reach, you can tell the

database to e-mail specific users on certain events... e.g. within a trigger, or when certain database tasks fail. With

a custom add-in, you can also use any SMTP server (see Article #2403). With Access, you would have to code this

stuff up yourself - assuming you find some way to trap the event(s) in the first place.

==================

Jobs

==================
SQL Server supports jobs, allowing you to schedule database tasks and have the system execute them automatically

(instead of a user having to invoke them). You can schedule jobs to be performed when the CPU is idle, or at certain

times during the day. We use this for number-crunching at the end of each day, and for archiving stats throughout

the day to keep our 'active' tables as small as possible.

==================

Stored Procedures

==================

Yes, Access supports stored queries. But IMHO, these are nowhere near as powerful as stored procedures. For one,

it is difficult to have stored queries access data from different databases. With stored procedures, this is trivial at

worst. SQL Server stored procedures support cursors and temporary tables, both of which are very powerful tools in

sorting data and performing queries. T-SQL also supports conditional logic, such as if / then and case blocks, as well

as index hints, locking hints and join hints. Most of these things are either extremely cumbersome or simply not

possible in the VB-bastardized version of SQL that ships with Access.

Additionally, SQL Server comes with several system and extended stored procedures, which you can plug into your

existing logic to do all sorts of things (such as retrieve a directory file listing (exec xp_cmdshell 'dir c:\'), list all of

the users currently accessing your database (exec sp_who2), or iterate through all DSNs on the server (exec

xp_enumdsn). Try and do those things from Access!

==================

Syntax Consistency

==================

Many Access developers are confused when they create a stored query that works fine within the Access

environment, but does not work from ASP or VB. For example, the function NZ() only works within Access. If you try

to use it from ASP or VB, you get an error that the function is not supported (see Article #2394). If you don't

discover this problem early on, you may have a lot of code to re-write.

==================

Again, if you want to use Access for your personal photos page or your CD collection, and you're not going to publish

it for the world to see, then Access is more than capable. I strongly recommend not using Access in any application

for which a 3rd party is relying on you, especially an e-commerce or other 24/7 operation.
And I'm not saying you have to use Microsoft's SQL Server, or even the latest version (I prefer 2000 over 7.0).

While it is the database that is the natural 'next step' - and many upsizing tools and tutorials are available (see

Article #2182); there are several other database packages you can look at, each with their own strengths and

weaknesses. These include SQL Server Desktop Edition, Sybase, DB2, Oracle, Informix... my preference, as you

might guess, is SQL Server... but I do have some level of faith in all of these products.

Finally, if you're stuck with Access, make sure you use the latest version of MDAC

(http://www.microsoft.com/data/), use a JET connection string (see Article #2126 for connection strings and Article

#2342 for JET downloads), and keep your database in good shape (see Q300216 - HOW TO: Keep a Jet 4.0

Database in Top Working Condition). Also, make sure your server has all the latest updates (see Article #2151).
Where can I get basic info about using stored procedures?

31,811 requests - last updated Monday, April 15, 2002

In this article, I will step through some INSERT, UPDATE and DELETE stored procedures, from the ground up. We will

explore a few speed bumps that I have experienced, and which you will most likely experience also.

This article assumes you're familiar with SQL Server and ASP, and have dbo-level privileges in Enterprise Manager.

Please be sure you have the latest version of Microsoft Data Access Components (MDAC) installed. If you're running

NT 4.0 or Windows 9x, you can download new versions at http://www.microsoft.com/data/. See Article #2057 to

determine which version you're currently running.

These samples were tested using the version of MDAC that ships with Windows 2000 (MDAC 2.5), as well as the

version that is installed by SQL Server 2000 (2.6 RTM).

Why use stored procedures?

1. Performance - stored procedures are pre-compiled by SQL Server, so performance is fast. The

increase in speed is similar to the benefits of porting lengthy blocks of resource-intensive ASP code

into a COM/COM+ DLL (without the overhead implications).

2. Isolation of Logic - stored procedures allow you to separate your data tier logic from presentation

code. This way, your ASP experts don't have to be SQL experts - and vice-versa. The SQL gurus can

often change the way a stored procedure works with data, without even having to touch the ASP

gurus' code. Granted, there are some times when this can add complexity to a project; for those

situations, I recommend using communication and SourceSafe, and remember #1.

When should I use stored procedures?

Our company policy is to encapsulate ALL data logic into stored procedures, and eliminate all direct SQL calls

(whether from ISAPI, COM, ASP, Servlets, or Java). For quick and dirty tests, you don't need to be 100%

formal about this. But for anything in production -- if you care about performance -- you should be using

stored procedures as much as possible.


How do I write stored procedures?

Stored procedures are created within SQL Server's Enterprise Manager GUI. You can create them from Query

Analyzer, or even from ASP code. Personally, I prefer the GUI for its "Check Syntax" button, but many

people think this is gratuitous and also find the space in the procedure GUI limiting. In any case, before we

get into the process, let's show a simple example of a stored procedure.

Note that your instinct may be to name a stored procedure with the sp_ prefix. Please do not do this, as this

prefix is reserved for system stored procedures. In addition to making your procedures confusing and

possibly opening the doors for pre-empting critical procedures used elsewhere in your code; even if you

choose a unique name, this causes a performance hit as the engine checks for that procedure name in the

Master database first. Accordingly, the procedures demonstrated in this article will be given the prefix FAQ_.

CREATE PROCEDURE dbo.FAQ_GetAuthors AS


BEGIN
SELECT lastname,firstname
FROM authors
ORDER BY lastname
END

This simply does a SELECT statement, returning authors' last and first names, ordered alphabetically by last

name. Notice the only syntactical difference between a typical SELECT statement and an equivalent stored

procedure is the CREATE PROCEDURE wrapper (and the BEGIN/END statements, which aren't altogether

necessary).

You can call this from ASP as follows:


<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC dbo.FAQ_GetAuthors")
do while not rs.eof
lastname = rs(0)
firstname = rs(1)
response.write lastname & ", " & firstname & "<br>"
rs.movenext
loop
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

This was a fairly trivial example; I'd like to go much deeper than this today. But first, let's examine some

questions you probably have about the above code.

■ What should my connection string look like?

To force TCP/IP over named pipes, and to avoid superfluous layers, here is a fictitious

example of the kind of connection string I always use:

<%
cs = "provider=SQLOLEDB; network=DBMSSOCN; server=1.1.1.1;"
' if you use a port other than 1433, use server=1.1.1.1,1500;
' (where 1500 is the port number SQL Server is listening on)
cs = cs & "database=pubs; uid=myUsername; pwd=myPassword;"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cs
' ...
%>

■ Why didn't I use a command object?

The command object brings overhead that is rarely necessary when executing any stored

procedure. This object has very stringent guidelines as far as creating parameters (including
strictly matching any and all data types being passed), and almost always forces you to

include ADOVBS.inc for constants (which is inefficient - see Article #2112). Some texts will

claim "it's faster, and simpler to use." While the former might be true in the case of a single

output parameter and no records, I've seen no proof of it otherwise... and I outright disagree

with the latter. Command object code is monstrous and very error-prone.

■ Why didn't I use a recordset object?

I avoid ADODB.Recordset for pretty much for the same reasons as avoiding

ADODB.Command. It has all kinds of overhead I very rarely require, and uses more complex

code to accomplish the same results. For more information, see Article #2191(and recall

that, in earlier versions of MDAC, an RS.Open call would tend to crash if it was an empty

resultset).

■ Why did I use rs(0) instead of rs("lastname")?

While retrieving recordset elements by name is more intuitive, it is actually much less

efficient than retrieving them by index number. Also, this almost forces you to assign them to

local variables, which aids in reuse and in retrieving text/memo fields. This, however, is one

of the few scenarios where you have to be in tune with the stored procedure developers... if

they change the SELECT field order, they have to tell you. When you work alone, this danger

isn't as great... but it might be one of those preference issues where readability outweighs

performance.

Let's move on to a more concrete example. We'll start with a fairly simple table. I created a table in pubs

called BOOKS. The fields are shown in the following table:

Field Name Data Type / Length Field Name Data Type / Length

id INT (Identity) synopsis VARCHAR (4000)

title VARCHAR (255) inprint BIT (DEFAULT=1)

pubdate DATETIME salesCount INT

I used one column to represent each datatype (and/or property) that I will be covering in this article.
DATETIME and VARCHAR(>255), in particular, seem to cause many problems for ASP developers. Here is the

script used to create the table from Query Analyzer, so you don't have to muck with the GUI:

CREATE TABLE BOOKS


(
id INT IDENTITY,
title VARCHAR(255) NOT NULL DEFAULT '',
pubdate DATETIME NOT NULL DEFAULT '1/1/1900',
synopsis VARCHAR(4000) NOT NULL DEFAULT '',
inprint BIT NOT NULL DEFAULT 1,
salesCount INT NOT NULL DEFAULT 0
)
GO

GRANT ALL ON BOOKS TO myUsername


GO

We make the id field an identity, so that it can be a unique, auto-incrementing value. Replace "myUsername"

with the true username you'll be using to connect with - the last line applies appropriate permissions (it

should have INSERT, SELECT, UPDATE and DELETE). Depending on the version of SQL Server you're using,

you may have to hit F5 before you see the new table in the Tables list.

INSERT

Now that we have a table created, we can start writing stored procedures around it. The first one we'll need,

of course, is an INSERT statement. After all, we need to get data in there somehow! A typical INSERT

statement for this table might be as follows:


INSERT INTO BOOKS
(
title,
pubdate,
synopsis,
salesCount
)
VALUES
(
'Seven Minute Stored Procedures',
'01/01/2000',
'This book helps you trim the fat, and feel good about coding again!',
0
)

One of the problems with running an INSERT statement directly is that you need to find some other means of

retrieving the IDENTITY value of the record you inserted. This is one of the most common questions asked in

Microsoft's public newsgroups, and it seems to cause people a lot of trouble (both in initially doing it, and - in

concurrent user scenarios - making sure that they are getting the proper IDENTITY value back, not someone

else's). A stored procedure makes this much more reliable, though admittedly not perfect. @@IDENTITY is a

*global* variable SQL Server uses to keep track of the IDENTITY value last inserted. While I have never

personally experienced a mistaken identity, I'm not going to go out on a limb and say it will never happen.

[Note: if you are running SQL Server 2000, replace all further instances of @@IDENTITYwith

SCOPE_IDENTITY(). This is a new method which makes sure you only get the IDENTITY value that *your*

connection generated.]

Changing this statement to a stored procedure will be pretty simple. Open Query Analyzer, copy the following

code, and hit F5:


CREATE PROCEDURE dbo.FAQ_InsertBook
@title VARCHAR(255),
@pubdate CHAR(10),
@synopsis VARCHAR(4000),
@salesCount INT
AS
BEGIN
SET NOCOUNT ON
DECLARE @newBookID INT
INSERT BOOKS
(
title,
pubdate,
synopsis,
salesCount
)
VALUES
(
@title,
@pubdate,
@synopsis,
@salesCount
)
SELECT @newBookID = @@IDENTITY
SELECT newBookID = @newBookID
END
GO

GRANT EXEC ON dbo.FAQ_InsertBook to myUsername


GO

Again, replace "myUsername" with the name of the account that will be accessing this application.

Let's review our code before we do anything with it. Always prefix stored procedures with dbo. This will

prevent permissions issues later on, and will prevent the awkward situation where two people, connected as

two different users, create stored procedures with the same name (yes, this is possible). We take in four

parameters: @title, @pubdate, @synopsis, and @salesCount. You'll notice I changed the datatype of
@pubDate; this is because we're only interested in the CHAR(10) equivalent of the date (10 characters, e.g.

"01/01/2000"). I've found that I run across far fewer problems when I avoid passing explicit datetime values

directly to stored procedures.[Footnote 1]

After the BEGIN command, we SET NOCOUNT ON -- this prevents interim statements from being returned as

recordsets.

I strongly recommend you always use SET NOCOUNT ON at the beginning of your stored procedures. This is

because, as you might learn soon enough, statements that return RowsAffected results (or any message, for

that matter) in Query Analyzer can also be interpreted as recordsets in client code -- which means the

recordset you think you're on isn't necessarily the right one. One example that dogged me for a few hours

went as follows:

INSERT ...
DELETE ...
SELECT ...

When I would call this stored procedure from an ActiveX DLL, I would try and obtain some fields from the

recordset... only to receive the "ordinal name not found" error. It took me a while to realize that using

NextRecordset() would eventually get me to the results I was after, but I didn't like this at all (the code was

inefficient, because it was returning much more data than it needed to be). Once I changed it to the

following, everything was fine:

SET NOCOUNT ON
INSERT ...
DELETE ...
SELECT ...

I have quickly become accustomed to wrapping non-result-returning code inside of SET NOCOUNT ON... to

avoid being bitten by this in the future. So, if you decide not to use the NOCOUNT options, at least (having

read this) you'll know what to look for if you experience similar problems.

Next we use the DECLARE command to create a temporary INT value called @newBookID. (While we could

do this without a temporary variable, this is a good habit to have - you will eventually be dealing with
multiple IDENTITY values in one stored procedure.) This value will store the IDENTITY number for the record

inserted by the stored procedure. The next 14 lines make up the INSERT statement, utilizing the four

parameters passed to the query - but otherwise looking like most INSERT statements you've seen before.

Note that we don't have to pass a value for the inprint column, since it has a default value of 1 - and we are

assuming that all books are, in fact, in print (we'll deal with overriding this value shortly).

The first SELECT statement in this query applies the global @@IDENTITY value (the latest insert) to the local

variable @newBookID.

All right, let's test this stored procedure before we move to ASP. In Query Analyzer, copy the following

query:

EXEC dbo.FAQ_InsertBook
@title='Seven Minute Stored Procedures',
@pubdate='01/01/2000',
@synopsis='This book helps you trim the fat, and feel good about coding again!',
@salesCount=0

When you execute the query, you should see the number 1 under the newBookID column in the results pane.

Next you'll see how we return this value, within a recordset, to our ASP page.[Footnote 2]

First, to be sure the record was inserted properly, change your query to the following:

SELECT * FROM BOOKS

After executing this query, you should see the new record intact, with all of the values you inserted. Now

that we know our stored procedure is running correctly, let's move to the fun part. The goal here, of course,

is to have ASP scripts running these things -- not to be fiddling with Query Analyzer and Enterprise Manager

all day long.

We'll start script similar to the ASP code above. I will populate local variables with values, but you can

assume that these values come from anywhere (e.g. the Form or QueryString collection). I have also

included my "FixDate" and "FixString" functions, which clean up dates and escape apostrophes, respectively.
<%
FUNCTION FixDate(str)
datestr = cdate(str)
mStr = month(datestr): dStr = day(datestr): yStr = year(datestr)
if (Clng(mStr)<10 and len(mStr)=1) then mStr = "0" & mStr
if (Clng(dStr)<10 and len(dStr)=1) then dStr = "0" & dStr
FixDate = mStr & "/" & dStr & "/" & yStr
END FUNCTION
FUNCTION FixString(str)
FixString = replace(str,"'","''")
END FUNCTION

title = "Six Minute Stored Procedures"


pubdate = "1/1/00"
synopsis = "This one is better than the last one."
salesCount = 0

title = FixString(title)
pubdate = FixDate(pubdate)
synopsis = FixString(synopsis)
salesCount = cLng(salesCount)

sql = "EXEC dbo.FAQ_InsertBook" &_


" @title='" & title & "'," &_
" @pubdate='" & pubdate & "'," &_
" @synopsis='" & synopsis & "'," &_
" @salesCount=" & salesCount
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute(sql)
Response.write("Record inserted. ID number was <b>" & rs(0) & "</b>.")
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

If you've gotten this far and are getting errors from the SQL Server driver, a good idea to try and debug is to

Response.Write the sql variable, so you can review on-screen EXACTLY what you're passing to the database

engine. It is often easier to spot a missing single quote or comma in a resulting string than in concatenated
code like the above sample. See Article #2145for more info.

UPDATE

Okay, so now we have data in there, what if we want to update it? Let's say the book 'Seven Minute Stored

Procedures' (ID = 1) reached a sales count of 4,023 units, and was shortly thereafter discontinued. We

would start with the same principle -- a basic UPDATE statement in T-SQL might look as follows:

UPDATE BOOKS SET


title = 'Seven Minute Stored Procedures',
pubdate = '01/01/2000',
synopsis = 'This book helps you trim the fat, and feel good about coding again!',
salesCount = 4023,
inprint = 0
WHERE
id = 1

A stored procedure that does this kind of update is extremely similar to the one we wrote earlier for inserting

a book. Here is the code:

CREATE PROCEDURE dbo.FAQ_UpdateBook


@id INT,
@title VARCHAR(255),
@pubdate CHAR(10),
@synopsis VARCHAR(4000),
@salesCount INT,
@inprint BIT = 1
AS
BEGIN
UPDATE BOOKS SET
title = @title,
pubdate = @pubdate,
synopsis = @synopsis,
salesCount = @salesCount,
inprint = @inprint
WHERE
id = @id
END
GO

GRANT EXEC ON dbo.FAQ_UpdateBook to myUsername


GO

The biggest difference here is that we have to pass the ID number as a parameter, so that SQL knows which

record we want to apply that change to (without a WHERE clause, it would update every record in the table).

Also note that if you DON'T pass a new inprint value, it will not change. The input param @inprint has a

default value of 1, so the value in the table will only change if you override it (which we will do in a moment).

You can test this new stored procedure by issuing the following command to Query Analyzer:

EXEC dbo.FAQ_UpdateBook
@id=1,
@title='Seven Minute Stored Procedures',
@pubdate='01/01/2000',
@synopsis='This book helps you trim the fat, and feel good about coding again!',
@salesCount=4023,
@inprint=0

And from ASP, the code for this new stored procedure would be almost identical to the previous example.

Let's assume, from now on, that you have placed the FixDate() and FixString() functions into an #INCLUDE

file called sqlFunctions.asp. The following file will update the synopsis of the book to reflect the fact that it is

no longer in print:
<!--#INCLUDE FILE='sqlFunctions.asp'-->
<%
id = 1
title = "Seven Minute Stored Procedures"
pubdate = "1/1/00"
synopsis = "This book was removed from circulation effective 6/1/2000."
salesCount = 4023
inprint = 0

id = cLng(id)
title = FixString(title)
pubdate = FixDate(pubdate)
synopsis = FixString(synopsis)
salesCount = formatnumber(cLng(salesCount),0,-1,-1,0)
inprint = cLng(inprint)

sql = "EXEC dbo.FAQ_UpdateBook" &_


" @id=" & id & "," &_
" @title='" & title & "'," &_
" @pubdate='" & pubdate & "'," &_
" @synopsis='" & synopsis & "'," &_
" @salesCount=" & salesCount & "," &_
" @inprint=" & inprint
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
conn.execute sql,,&H00000080
Response.write("Record updated.")
conn.close: set conn = nothing
%>

You're probably asking "what's that &H00000080 for?" This is the constant for adExecuteNoRecords. It tells

the SQL engine that you won't be returning or processing any rows, and executes the query more efficiently -

providing a bit more performance boost to your application. My testing yielded gains better than 20% simply

by adding this constant to my execute calls in ASP and VB. Since this stored procedure didn't return any

data, we didn't need to create a recordset object at all; we simply passed the SQL statement directly to the

execute method of the connection object. One issue you'll want to watch for is passing larger integer values
to SQL statements. Because a large portion of T-SQL syntax relies on commas, you have to be careful not to

pass numbers formatted with comma separators (which is why the above salesCount assignment has the

additional formatnumber command - in case you're passing to it a value with comma separators).

DELETE

Now we'll get into a slightly more complex example. Let's say we want to delete all records where the word

"seven" appears in the title (hey, I didn't say these examples had to be logical!). The typical DELETE

statement would look like this:

DELETE FROM BOOKS


WHERE
title LIKE '%seven%'

A common requirement for this kind of query is that it alerts the user how many records were actually

deleted. When using a normal DELETE statement, you would have to do a differential between SELECT

COUNT(id) before and after the DELETE query is run. This makes for messy and inefficient code, and is not

entirely intuitive either. Luckily, SQL Server has another global variable which, like @@IDENTITY, is used to

keep track of specific values. This variable, called @@ROWCOUNT, reflects the number of rows affected by a

query.

And this brings us to one of the challenges with using @@ROWCOUNT. Any value returned to it is

automatically set to 0 when SET NOCOUNT ON is used in conjunction with statements that don't return any

rows (such as IF or DELETE statements). Ultimately, this means we're going to have to examine returning

multiple recordsets from a stored procedure. Don't get me wrong, it is not a bad thing to learn. But it is one

of the reasons this example is slightly more complex than the previous examples.

Before we get too far, let's see what the stored procedure looks like. It is actually quite similar to the

previous stored procedures, with far fewer parameters being passed:


CREATE PROCEDURE dbo.FAQ_DeleteBook
@criteria VARCHAR(255)
AS
BEGIN
DELETE FROM BOOKS
WHERE
SUBSTRING(LOWER(@criteria),LOWER(title))>0
SELECT @@ROWCOUNT
END
GO

GRANT EXEC ON dbo.FAQ_DeleteBook to myUsername


GO

Executing this query from Query Analyzer would look like this:

EXEC dbo.FAQ_DeleteBook @title='seven'

You can use FAQ_InsertBook in the Query Analyzer to populate the BOOKS table with a bunch of identical

records, then run FAQ_DeleteBook to make sure the proper number of rows are being returned. When you

are sure the stored procedure is working as advertised, we can move to ASP. The biggest difference in the

following code, compared to the resultset example from the INSERT stored procedure, is that we call the

NextRecordSet() method of the rs object (see the boldsection below).

<!--#INCLUDE FILE='sqlFunctions.asp'-->
<%
title = "seven"

sql = "EXEC dbo.FAQ_DeleteBook @title='" & FixString(title) & "'"


set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute(sql)
set rs = rs.NextRecordSet()
Response.write(rs(0) & " rows deleted.")
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

The first "recordset" doesn't return any data (but is defined as a recordset by the provider), so we don't need

to care about any results. In fact, trying to access any element in the first recordset will result in an ADO

error. Once you move to the second recordset, you can safely retrieve the number of records that the query

deleted (and this is why, in this case, we don't use the adExecuteNoRecords constant).

Note that most DELETE queries would delete by ID number or some other unique identifier; all it takes is

someone passing a criteria value of 'a' to delete just about every book in your database. These are just

simple examples that try to cover all the little issues that invariably come up when using stored procedures.

Conclusion

As you can see, there are many pros and cons to using stored procedures. The main advantage are

performance, additional functionality and encapsulation of code; while a significant disadvantage is that there

are a few more considerations to make when writing code. Overall, it is the author's opinion that stored

procedures are worth the trouble - several times over. You owe it to yourself to try them out, and see for

yourself how much they can improve your database's performance, as well as your development schedule.

Footnotes

[1] One thing you'll want to make sure of is that your ASP pages are actually creating dates in MM/DD/YYYY format. Server setup registers MM/DD/YY

as the default system locale (as ridiculous as this is after that whole Y2K thing); so, even if you have changed the format to MM/DD/YYYY for a specific

user, if nobody is logged in to the box, it reverts to the default. I have yet to find a reliable way, short of reinstalling from scratch, of reversing that

setting (and I have argued with my fair share of WPP "tech support" people about it). To be sure my ASP scripts are always using proper CHAR(10)

date format no matter who is logged in, I wrote the following VBScript function - which I place in all of my top-level includes (I've also included a

simple test to verify that it works):


FUNCTION FixDate(str)
datestr = cdate(str)
mStr = month(datestr): dStr = day(datestr): yStr = year(datestr)
if (Clng(mStr)<10 and len(mStr)=1) then mStr = "0" & mStr
if (Clng(dStr)<10 and len(dStr)=1) then dStr = "0" & dStr
FixDate = mStr & "/" & dStr & "/" & yStr
END FUNCTION

' As a test:

SQL = "EXEC dbo.myProc @dateVal='" & fixDate("2/1/74") & "'"


Response.Write(SQL)

[2] Yes, using an output parameter can be slightly more efficient than returning a recordset. However, this is one of those cases where ease of use

often outweighs performance concerns, IMHO (output parameters, like ADODB.Command, can be a pain to use). And further to that, I find it more

often than not the case that I'm returning multiple recordsets from a stored procedure (as opposed to the rare case of only one value being returned),

whereby you lose any performance advantage of the parameter anyway.


Why do I get database-related 80004005 errors?

29,407 requests - last updated Tuesday, December 10, 2002

(We split up this article into two - one involving database-related issues, the other encompassing everything else. If

your error does not involve a database, please see Article #2413.)

First off, test connecting to your database *without* any sort of DSN. See Article #2126 for sample DSN-less

connection strings. Removing a flawly configured DSN might solve any 80004005 errors, but if nothing else, it will

improve the performance and scalability of your application.

Next, make sure you have the most recent MDAC (http://www.microsoft.com/data/). If you are using Access, make

sure your database is in 2000 or 2002/XP format, and that your servers have the most recent JET drivers (see

Article #2342). In addition, make sure the anonymous user on your machine (IUSR_machineName) has both read

and write access to the folder in which the MDB file(s) reside. Finally, make sure your server is up-to-date (see

Article #2151 for recent information on service packs and security fixes).

That said, in the remainder of the article we will highlight some of the more common 80004005 errors, and attempt

to explain and/or solve them.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver] The Microsoft Jet database engine
cannot open the file '(unknown)'. It is already opened exclusively by
another user, or you need permission to view its data.

or

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC Microsoft Access Driver]
'(unknown)' isn't a valid path. Make sure that the path name is spelled
correctly and that you are connected to the server on which the file
resides.

or
Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[Microsoft][ODBC Microsoft Access 97 Driver] Couldn't use '(unknown)'; file
already in use.

This usually means that the MDB file is open in a copy of Access on the server, or Visual InterDev is open and has an

active connection to the database. For more information, see Q174943. This can also mean you are attempting to

connect to an MDB file on a remote share; if so, see Article #2168 and Q189408 for more information. See Q315456

and Q251254 for information on correcting the permissions on your %TEMP% folder. Finally, see Q166029 for

information on configuring credentials for authenticated users connecting to an Access database file. For other

possible reasons, see Q306269.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver] Cannot open database '(unknown)'.
It may not be a database that your application recognizes, or the file may
be corrupt.

or

Microsoft JET Database Engine error '80004005'


Unrecognized database format '<file>.MDB'

One possibility is that the file is corrupt (Access databases have a tendency to do that -- see Article #2190 for a way

to compact / repair the database through code). A more common reason is that you are trying to connect to an

Access 97 database with Access 2000 or better drivers, or vice-versa. Make sure the server has the most recent

MDAC (http://www.microsoft.com/data/) and the most recent JET drivers (see Article #2342). Also, try upgrading

your database to a more recent format (Access 2000 or better).

Microsoft JET Database Engine (0x80004005)


Could not find installable ISAM.

For information on this error, see Article #2259.


Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[Microsoft][ODBC Driver Manager]Data source name not found and no default driver specified.

This usually happens in one of the following scenarios:

■ you referenced your connection incorrectly (e.g. spelled the DSN name, or one of the DSN-less string

components wrong);

■ you referenced a DSN that doesn't exist;

■ the user connecting to the DSN or DSN-less connection doesn't have access to the information stored in the

registry (see Q306345);

■ you used an English or localized driver detail for your connection string when your system is not set up in

that language (see Q174655); or,

■ you are missing the connection string entirely (this can happen if you maintain your connection string in a

session variable, and your sessions aren't working; see Article #2157).

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access 97 Driver]
Operation must use an updateable query.

or

Microsoft JET Database Engine (0x80004005)


Operation must use an updateable query.

This is almost always a permissions issue, where IUSR_machineName or the autheticated user doesn't have read or

write access to the MDB file, the folder in which it is located, or the TEMP/TMP folders. For more information, see

Article #2062.
Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[Microsoft][ODBC Microsoft Access 97 Driver] Couldn't lock file.

This usually happens because the anonymous or authenticated user does not have write permissions to create the

LDB file (this is Access' 'scratch' file). Make sure IUSR_machineName (or the user(s)/group(s) accessing the

application) have write permissions on the folder where the MDB file located.

Microsoft Cursor Engine (0x80004005)


Data provider or other service returned an E_FAIL status.

Make sure you are using the most recent MDAC (from http://www.microsoft.com/data/) and, if using Access, the

most recent JET drivers (see Article #2342). See Q249638.

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC Microsoft Access Driver] The Microsoft Jet database
engine stopped the process because you and another user are attempting
to change the same data at the same time.

This is actually often due to a corrupt database, when a table has long value columns (which are stored off-row). To

fix, either run a compact / repair on the MDB file (see Article #2190 for information on doing so from ASP), or

import all of its objects into a new MDB file and replace the existing file.
Microsoft OLE DB Provider for ODBC Drivers (0x80004005)
[Microsoft][ODBC Microsoft Access Driver]General error Unable to open
registry key 'Temporary (volatile) Jet DSN for process 0x9ac Thread 0xa0c
DBC 0x15d1024 Jet'.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver]General error Unable to open
registry key 'DriverId'.

or

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC Microsoft Access Driver] Disk or network error.

or

Microsoft JET Database Engine error '80004005'


Disk or Network error

or

Provider error '80004005'


Unspecified error

or

Microsoft JET Database Engine error '80004005'


Unspecified error

Again, IUSR_machineName must have read and write permissions not only on the MDB file, but also on the folder in

which it resides, and in some cases the system %TEMP% folder. See Article #2154 and Q315456 for more

information. If you are trying to access a network drive, see Article #2168 for information on setting up

IUSR_machineName to connect to an Access database via a UNC share. You should also check out Article #2142 for

information on opening Access in the correct mode (e.g. adModeReadWrite).


Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[Microsoft][ODBC Driver Manager] Driver's SQLSetConnectAttr failed

See Article #2220 for a description of this informational message.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][DBNMPNTW]
ConnectionOpen(CreateFile()).

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][TCP/IP Sockets]
SQL Server does not exist or access denied.

or

Microsoft OLE DB Provider for SQL Server error '80004005'


[DBNETLIB][ConnectionOpen (Connect()).]
SQL Server does not exist or access denied.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][Named Pipes]
Specified SQL server not found.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver]
Client unable to establish connection.
While the first error message is definitely more cryptic than the others, they all mean the same thing. The first

simply uses DBNMPNTW, which is the protocol name for named pipes. What the error means is that the SQL Server

you defined in your connection string is invalid - either because it is currently down, you named it wrong (either by

hostname, DNS/domain or IP address), or you don't have permission to connect. If you are expecting to connect by

IP address, make sure you override named pipes (see Article #2082). If you are using SQL Server authentication,

make sure both ends are set up for it (see Article #2138). If the error is 'SQL Server does not exist or access

denied', see Q328306, which has a large variety of possible causes and solutions. If the ASP page and SQL Server

are on the same machine, then it may be a loopback problem - try using 127.0.0.1 or LOCALHOST (instead of the

external IP or network name) in the connection string.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Driver Manager]Driver's SQLAllocHandle on SQL_HANDLE_ENV failed

This usually happens with Oracle, when using a DSN or a misconfigured OLEDB connection string. To solve, drop-

kick the DSN. Try using the OLEDB providers instead of using ODBC. First, try the Microsoft-provided OLEDB

drivers:

connStr = "Provider=MSDAORA.1;User ID=<userid>;" & _


"Password=<password>;Data Source=<server>.<dbname>;"

If that doesn't work, download the Oracle OLEDB provider and use this connection string:

connStr = "Provider=OraOLEDB.Oracle;User ID=<userid>;" & _


"Password=<password>;Server=<server>;Data Source=<dbname>;"

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][SQL Server] xp_sendmail: failed with mail error
0x80004005.
You have incorrectly configured SQL Mail. See if any of the following KB articles help: Q263556, Q274330, Q279867,

Q293422, Q312839, Q315886, Q321183.

Also, be sure to consider using XP_SMTP_SendMail (see Article #2403).

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][SQL Server] Login failed- User: Reason:
Not defined as a valid user of a trusted SQL Server connection.

If you are trying to use Windows Authentication to connect to SQL Server, make sure the account being used has

been mapped to a valid SQL user or role, and that the user has been authenticated correctly (see Article #2126 and

Q306586).

Microsoft OLE DB Provider for SQL Server (0x80004005)


Your transaction (process ID #<spid>) was deadlocked with another process
and has been chosen as the deadlock victim. Rerun your transaction.

This can occur if you use an isolation level of READ COMMITTED or READ UNCOMMITTED and have multiple

concurrent connections attempting to run the same procedure or batch. Consider using REPEATABLE READ or even

SERIALIZABLE, realizing that this will be a performance hit (usually sending lock blocks through the roof).

Otherwise, try and make your T-SQL code more efficient, so that there is less chance that two unique users will trip

over each other.


Microsoft OLE DB Provider for SQL Server error '80004005'
[DBMSSOCN]General network error. Check your network documentation.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][TCP/IP Sockets]General network error.
Check your network documentation.

or

Microsoft OLE DB Provider for SQL Server error '80004005'


[DBNETLIB][ConnectionOpen (PreLoginHandshake()).] General network error.

This is often a firewall / router issue. Test that your web server can ping the SQL Server (both by name and IP) and

make sure that your connection string points to a valid IP address or network name.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver]
Received an unrecognized datatype 0 from TDS data stream

or

Unknown token received from SQL Server

or

Protocol error in TDS stream

or

Bad token from SQL Server: Datastream processing out of sync.

or
Invalid cursor state

or

TDS Buffer Length Too Large

or

TDS Buffer Link Too Large

or

Function sequence error

This is usually an intermittent error... it will happen every once in a while, and can be attributed to perhaps a flaky

network connection or an isolated network jam. If the error message(s) persist, see the following suggestions:

If you are using an ODBC connection, see Q176256 - and consider using OLEDB provider instead of the native SQL

Server driver (see Article #2126).

If there is a specific stored procedure causing this problem, try adding these lines to the beginning of the proc,

running it once, and then commenting out or removing the NO_BROWSETABLE line:

SET NO_BROWSETABLE OFF


SET NOCOUNT ON

If you are querying system tables, use a forward-only cursor (to be safest, avoid ADODB.Recordset altogether).

If you are using SQL Server 7.0: if you have ANSI_WARNINGS set to OFF, see Q259775; if you are using the

ROUND() function, make sure that the parameters you are sending are not null (see Q199105)

If you are using SQL Server 2000, and are using SELECT DISTINCT on a table with a LEFT JOIN on a view, upgrade

to SQL Server 2000 Service Pack 2 (see Q308547).

If you are using MTS transactions, try to change your approach by using transactions within SQL Server.
If none of the above solves your issue, see Q243899.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][DBNMPNTW] ConnectionWrite
(GetOverLappedResult()).

or

Unable to insert record...error number -2147467259: MS ODBC SQL Server Driver dbnmpntw.
ConnectionWrite (getoverlappedresults(). This may be caused by an attempt to update a
non-primary table in a view.

or

The query is not updatable because the from clause is


not a single simple table name. This may be caused by an attempt to
update a non-primary table in a view.

One potential solution, according to Q178040, is to use TCP/IP instead of named pipes. Or, according to Q186726,

use a local connection string, instead of a network string. Personally, I've never found any reason to use Named

Pipes in a web environment. See Article #2126 for sample DSN-less connection strings that avoid using Named

Pipes.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC SQL Server Driver][SQL Server] Invalid object name '<object name>'

This usually means that the object name was misspelled or doesn't exist, or the user connecting to the database

cannot see the object because they do not have appropriate object-level permissions. For more information, see

Article #2164.
Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[Microsoft][ODBC Microsoft Access Driver] Syntax error in UPDATE statement.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver] Syntax error in INSERT INTO statement.

Make sure you are not using any reserved words (e.g. Date or Password) as column names ... if you cannot change

the column names, use [] brackets around them (see Article #2080 for a list of reserved words). Ensure that your

datatypes are appropriately delimited (e.g. no delimiters for numbers, ' for strings (and dates in SQL Server), # for

dates in Access. Also, try to avoid using rs.AddNew / rs.Update -- use a direct INSERT or UPDATE statement instead.

This is more efficient and easier to debug (see Article #2191).

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver] Too few parameters. Expected n.

This usually happens when your parameters are empty, or strings aren't properly delimited, e.g. in the following

code:

<%
myString = ""
sql = "INSERT INTO table(column) VALUES('" & myString & "')"
conn.execute(sql)

myString = "foo"
sql = "INSERT INTO table(column) VALUES(" & myString & ")"
conn.execute(sql)
%>

Of course, you'll want to fix this problem by making sure you're not inserting empty or non-delimited strings.
Microsoft OLE DB Provider for SQL Server (0x80004005)
Invalid connection string attribute

Double-check your connection string. This is usually caused by invalid characters or empty parameters in the

connection string. See Article #2126 for examples of valid connection strings.

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access Driver] The changes you requested to the
table were not successful because they would create duplicate values in the
index, primary key, or relationship. Change the data in the field or fields
that contain duplicate data, remove the index, or redefine the index to
permit duplicate entries and try again.

This is the most verbose error message I've seen from Microsoft to date. It seems pretty self-explanatory; you need

to look at your query, and look at your data, and determine why one of these relationships or constraints would be

violated by committing the INSERT / UPDATE you are attempting.

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC SQL Server Driver][SQL Server]SqlDumpExceptionHandler:
Process <spid> generated fatal exception c0000096 EXCEPTION_PRIV_INSTRUCTION.
SQL Server is terminating this process.

This error message indicates that the spid attempted to execute an instruction that is currently not allowed. Check

your SQL Server error logs for access violations and other errors. Disable any startup procedures and make sure the

server starts cleanly. Check the log folder for .dmp files that may have happened around the same time.
Microsoft OLE DB Provider for Analysis Services (0x80004005)
Cannot connect to the server '<server>'.
The server is either not started or too busy.

Make sure the services MSSQLServer and MSSQLOLAPServices are both running.

Microsoft OLE DB Provider for Analysis Services (0x80004005)


The cube '<cube>' does not exist, or it is not processed

You are either attempting to access Analysis Server while it is preparing the cube, or you are referencing a cube that

does not exist.

Microsoft OLE DB Provider for Analysis Services (0x80004005)


Database '<database>' does not exist.

You are either attempting to connect to AS while a a database is being restored, or the initial catalog in your

connection string is incorrect.

Microsoft OLE DB Provider for Analysis Services (0x80004005)


Access denied to the cube <cube>.

You are attempting to access a cube that has been processed but not yet had roles applied; or, you are accessing a

cube with a user that does not have proper permissions.

Finally, Q306518 contains a listing of many 80004005 error messages, the most frequent causes of the error

messages, and troubleshooting steps to resolve them.

If you are using DB2 / AS/400


Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[IBM][Client Access Express ODBC Driver (32-bit)][DB2/400 SQL]
SQL0666 - Estimated query processing time <x> exceeds limit <y>.

AS400 imposes a limit (through QQRYTIMLMT) on queries... and it will refuse to run any query which appear,

through estimation, that they will take longer than y seconds.

Increase the commandTimeout on the connection object to some value higher than x (give yourself some buffer

because the estimated query plan might change). For information on changing the commandTimeout value, see

Article #2066.

If you are using SQLBase

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[MERANT][ODBC SQLBase driver]Optional feature not implemented.

Check out this thread on Google, it may be of help:

http://tinyurl.com/p0k

(We will be covering MySQL and Oracle 80004005 articles in a forthcoming update.)

If you know of any other 80004005 errors that we haven't covered here, please let us know...
How do I upload images to a database?

27,808 requests - last updated Saturday, August 10, 2002

While I *strongly* recommend storing the file in the filesystem and its LOCATION in the database, there are several

components out there that make this really easy. My preference is ASPUpload from Persits Software. You can see

how simple the code is by looking at http://www.aspupload.com/manual_db.html.

You can also handle this type of task without a component, but it's a lot more code. There are good examples

available at ASP 101 and StarDeveloper.

Here are the arguments for each methodology.

Keep images on the filesystem, and the location and other data in the database

1. if there is any chance that you will migrate to a different database platform, your current BLOB format might

be incompatible with, or at least a pain to convert to, the new format -- since, like web browsers, each

vendor has implemented things with their own slant.

2. when your database really goes south, to the point where even the backup is useless, you still have the files

on the filesystem (though their usefulness is questionable, depending on how much related data was kept in

the database). Which is arguably better than losing all of your data *and* all of your files.

3. having the images in the file system allows you to access the images from many different standard

applications (FTP, web browser, etc) without having to write application code to pull the data out of the

database, since you can't just 'SELECT image FROM table' and have the image appear in Enterprise Manager

or Query Analyzer.

4. with some databases, e.g. Access and MSDE, the data inside is limited to 2 GB, whereas the file system is

not as restricted. Also, most hosts charge a premium for SQL Server storage space, so in that case it would

be cheaper to store them in the file system.

5. in SQL Server, when a table has an IMAGE or TEXT column, a page is reserved for every row, whether or not

that column has any data... so if you don't have an image for each record, there is potential to have a lot of

wasted space. There are workarounds to this, of course, such as splitting the image data into its own table

with a foreign key to the main table. Also, in SQL Server 2000, if your images are less than 8kb, you can use

the text in row option by running the following code:

EXEC sp_TableOption N'TableName', 'TEXT IN ROW', 'ON'


Of course you will have to be careful with this option if you have other TEXT / IMAGE columns in the table.

6. performance wise, including an <IMG SRC> tag generated by the database and pointing to a file that

already exists is going to be faster than pulling the file out of the database, generating a temp file on the

web server, and streaming that to the user. Also, table scans take more resources when there is an image

datatype as opposed to a varchar that simply holds a 'pointer' to the file's location.

7. can be quite complicated extracting images, say from an Access database, since it adds OLE header info to

the file (see Q175261). With SQL Server it's not so bad; see Q173308 for an example that works right out of

the box. You can also see Q194975 for samples that use ADO's GetChunk and AppendChunk methods.

Keep the images and associated data in the database

1. you can protect your images better with SQL Server and file system security, as opposed to simply file

system.

2. it can be challenging to keep database and filesystem in sync with each other - if the record is deleted in

SQL Server (e.g. through an ad hoc query, or a rollback) how does the file system portion of the application

know to also delete the image in the file system? One approach is to use extended stored procedures in SQL

Server to encompass the file system operations into the database transactions, however this can become as

complex, or more so, than storing the BLOB directly in the database - which gives you greater transactional

control and consistency.


How do I make DSNs without calling my ISP?

27,512 requests - last updated Friday, September 27, 2002

Unless you can log into the ISP's machine with remote software (which is typically only allowed when you're leasing

an entire box), you can't. You need to have your ISP set up your DSN(s), which certainly takes time, and sometimes

costs money (depending on the host). Also, you can only manage so many DSNs reasonably (see Q252475). Using a

DSN-less connection (for example, those found in this article), you can avoid this hassle *and* make your data

access faster.

SQL Server

Using SQL Server Authentication:

<%
cst = "Provider=SQLOLEDB;Server=<ip address>;database=<dbname>;"
cst = cst & "network=DBMSSOCN;uid=<uid>;pwd=<pwd>"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
' // Use of Network=DBMSSOCN is to avoid Named Pipes errors
%>

To use SQL Server authentication, SQL Server must be set up in 'mixed mode' (both SQL Server and Windows

Authentication) and you must have a SQL Server login with appropriate permissions on the database(s) you are

connecting to.

Using Windows Authentication:

<%
cst = "Provider=SQLOLEDB;Data Source=<Server Name>;"
cst = cst & "Initial Catalog=<dbname>;Integrated Security=SSPI"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>
Note that to use Windows Authentication, IUSR_<machineName> must be in the domain, and given proper access

to the SQL Server; or, you must disable anonymuos access on the site / application - which will allow IIS to pass the

users' credentials to SQL Server. Not doing either of these things will result in the following error:

Microsoft OLE DB Provider for SQL Server error '80040e4d'


Login failed for user '<machineName>\IUSR_<machineName>'. Reason:
Not associated with a trusted SQL Server connection.

For information about configuring SQL Server for access through IIS, see Q247931.

SQL Server Analysis Services

<%
strASConn = "PROVIDER=MSOLAP;" & _
"DATA SOURCE=<ip>;" & _
"INITIAL CATALOG=<database>"

or

strASConn = "PROVIDER=MSOLAP.2;" & _


"DATA SOURCE=<ip>;" & _
"INITIAL CATALOG=<database>"

' and then

set ASCellset = Server.CreateObject("ADOMD.Cellset")


ASCellset.ActiveConnection = strASConn

or

set ASCatalog = Server.CreateObject("ADOMD.Catalog")


ASCatalog.ActiveConnection = strASConn
%>

Oracle
If you are using the Microsoft OLEDB provider for Oracle:

<%
cst = "Provider=MSDAORA.1;User ID=<uid>;Password=<pwd>;"
cst = cst & "Data Source=<server>.<dbname>;"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>

If you are using the Oracle OLEDB Provider (which you can download after registering here):

<%
cst = "Provider=OraOLEDB.Oracle;Server=<server>;Data "
cst = cst & "Source=<dbname>;User ID=<uid>;Password=<pwd>"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>

Microsoft Access

<%
cst = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
cst = cst & server.mappath("/<pathtofile.mdb>")
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>

If you don't have JET 4.0 installed, consider installing it (see below). Otherwise, you can use the native ODBC driver

for Access:
<%
cst = "Driver={Microsoft Access Driver (*.mdb)};DBQ="
cst = cst & server.mappath("/<pathtofile.mdb>")
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>

In each case, of course, filling in the <variables> with the proper values.

Regarding using JET over ODBC, according to Q222135:

"When running Microsoft Jet in an IIS environment, it is recommended that you use the native Jet OLE DB Provider

in place of the Microsoft Access ODBC driver. The Microsoft Access ODBC driver (Jet ODBC driver) can have stability

issues due to the version of Visual Basic for Applications that is invoked because the version is not thread safe. As a

result, when multiple concurrent users make requests of a Microsoft Access database, unpredictable results may

occur. The native Jet OLE DB Provider includes fixes and enhancements for stability, performance, and thread

pooling (including calling a thread-safe version of Visual Basic for Applications)."

See Article #2342 for information on obtaining the latest version of the JET OLE-DB drivers.

Others

For a more exhaustive list of connection string types, see http://www.able-consulting.com/ADO_Conn.htm, where

Carl Prothman has compiled several different techniques of connecting to just about any data source -- from DB2 to

Sybase to MySQL to Visual FoxPro...

Keep in mind that using an invalid connection string can lead to the following errors:
Microsoft OLE DB Service Components error '80040e73'
Format of the initialization string does not conform to the
OLE DB specification.

or

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Driver Manager]
Data source name not found and no default driver specified.
How do I deal with MEMO, TEXT, HYPERLINK, and CURRENCY columns?

25,586 requests - last updated Friday, August 23, 2002

MEMO / TEXT / VARCHAR(>255)

Depending on the version of your MDAC driver, and the database you are connecting to, these columns can either

(a) not show up at all, (b) only show up the first time they're called, or (c) cause 'Unspecified Error', 'Exception

Occured' or 'Errors Occurred' runtime errors, if the following recommendations are not observed:

■ Avoid SELECT * notation; NAME your columns in a list, and name the offending column(s) LAST.

■ Assign the value of the column to a variable IMMEDIATELY, and only use this variable from that point on.

■ Make sure your MDAC drivers are most current (http://www.microsoft.com/data/).

■ See Q200124 and Q175239

Another problem people have is retaining line breaks from a textarea, once the value is returned to the screen.

Access and SQL Server DO store this information, it's just not in a format that a browser's HTML engine

understands. You need to replace line feeds and carriage returns with HTML tags.

Here is some example syntax:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open <connection string>
sql = "SELECT idcolumn,intcolumn,memocolumn FROM table"
set rs = conn.execute(sql)
do while not rs.eof
memocolumn = rs("memocolumn")
response.write(replace(memocolumn, CHR(10), CHR(10) & "&nbsp;<br>"))
rs.movenext
loop
%>

Hyperlink, Currency, MONEY


Many people assume that, because Access can understand that a hyperlink column should be underlined and

clickable and that a currency column starts with a dollar sign and is followed by two decimal places, the browser

should be able to as well. So they write syntax like this:

<%
response.write(rs("hyperlink_column"))
response.write(rs("currency_column"))
%>

And expect the browser to automatically wrap the hyperlink in an anchor tag, and format the currency column

appropriately. Unfortunately, this isn't how it works. HTML doesn't know what a hyperlink is, unless you tell it to use

an <A> tag. Similarly, HTML doesn't automatically format your numbers and assume you want thousands separators

and dollar signs.

DO NOT use the hyperlink datatype in Access. This is designed to allow users to click on links in the GUI. It is NOT

meant for presentation-layer formatting (e.g. HTML). Use a VARCHAR column for hyperlinks. I would also

recommend using a numeric data type with two decimal places for currency; in SQL Server, use the MONEY

datatype. Storing dollar signs and #link# values in the table aren't going to help you in HTML, and only add

unnecessary storage size to your tables.

Based on that, to format these values in your HTML, you need to do something like this (from Access)):

<%
hl = rs("hyperlink_column")
response.write "<a href='" & hl & "'>" & hl & "</a>"
curr = rs("currency_column")
response.write formatcurrency(curr,2)
%>

To get a hyperlink formatted properly right out of SQL Server, you can use:
SELECT
'<a href='+hyperlink_column+'>'+hyperlink_column+'</a>'
AS hyperlink_column
FROM table

To get a money value formatted properly right out of SQL Server, you can use:

SELECT
'$'+CONVERT(VARCHAR(100), money_column, 1)
AS money_column
FROM table

Some people have reported that this doesn't properly insert commas (I assume it has to do with collation or regional

settings on the server). So, to force it using a bunch of wacky string manipulation, you can try something like this:

SELECT

'$'+REVERSE(SUBSTRING(REVERSE(CONVERT(VARCHAR(100),CONVERT(MONEY,money_column),1)),1,100))
AS money_column
FROM table

Please let us know if this still doesn't work in your environment.


Why does RecordCount return as -1?

24,642 requests - last updated Wednesday, January 30, 2002

Recordcount is not supported with the default forward-only cursor. If you open a recordset in the following manner,

you will have access to recordcount:

<%
numrecords = 0
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = Server.CreateObject("ADODB.Recordset")
sql = "SELECT whatever FROM table WHERE [...]"
rs.open sql,conn,1,1
if not rs.eof then numrecords = rs.recordcount
response.write("There were " & numrecords & " matches.")
...

However I think this is more efficient (even though it looks like more code):

<%
numrecords = 0
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
sql = "SELECT COUNT(field) FROM table WHERE [...]"
set rs = conn.execute(sql)
if not rs.eof then numrecords = rs(0)
sql = "SELECT whatever FROM table WHERE [...]"
set rs = conn.execute(sql)
response.write("There were " & numrecords & " matches.")
...

And in SQL Server, this would be a more direct approach:


<%
numrecords = 0
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
sql = "SELECT whatever FROM table WHERE [...]" & vbCrLf
sql = sql & "SELECT @@ROWCOUNT"
set rs = conn.execute(sql)
set rs2 = rs.nextrecordset()
if not rs.eof then numrecords = rs2(0)
response.write("There were " & numrecords & " matches.")
...

Keep in mind that the condition in both queries [...] should be identical!
How do I connect to an Access database / text file on another web server?

23,130 requests - last updated Friday, August 23, 2002

If the file is outside your LAN

Access isn't really designed for this type of application (see Article #2195 for more info), and if you need your

database server to reside on a separate machine from your web server, it might be time to look for a more scalable

database (e.g. SQL Server or Oracle). However, there is a way to connect to an Access database over the web,

assuming you know the absolute file structure of the remote server (and know this won't change). Keep in mind that

Access is not the most prudent performer on the same machine as the web site, and is likely to be more problematic

over the web (which has many more variables). Here is a sample connection string:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "Provider=MS Remote;" &_
"Remote Server=http://<ip address or server name>;" &_
"Remote Provider=Microsoft.Jet.OLEDB.4.0;" &_
"Data Source=c:\inetpub\wwwroot\file1.mdb;"
%>

I have received feedback that this "doesn't work" -- while I have no idea what "doesn't work" means, I can say that

along with the performance issues of using this kind of solution, there is also a painful configuration process that

must be followed. NTFS permissions, firewalls, existence of MSADC, and several other issues might prevent this from

working. The most common error I have seen is:

Error Type:
Microsoft ADO/RDS (0x800A20FF)
Internet Server Error.

If you are getting this error, please refer to the following KB articles: Troubleshooting Common Problems with

Remote Data Services (Q251122) and HOWTO: Use RDS From an IIS 4.0 Virtual Server (Q184606).

Other permissions problems might be solved by referring to Q253580...


For text files, you can read the contents of remote text files by using XML or one of the many ASP components

designed for performing an HTTP "tear" - see Article #2173 for more info and a detailed example.

If the file is within your LAN

If you are using Windows Authentication, see Q197964 (PRB: Cannot Access Remote Files with the

FileSystemObject).

If you are using anonymous access, you can accomplish this by synchronizing the anonymous user accounts on the

two machines. Read on.

Let's say you have MachineA and MachineB. MachineB has a text file or Access database, in a share, that you want

to have control over from your ASP application on MachineA. With a stock server setup, you should get one of many

80004005 errors with Microsoft Access, most commonly:

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access 97 Driver]
The Microsoft Jet database engine cannot open the file '(unknown)'.
It is already opened exclusively by another user, or you need
permission to view its data.

Or one of the following errors with FileSystemObject:

Microsoft VBScript runtime (0x800A0046)


Permission denied
or
Microsoft VBScript runtime (0x800A004C)
Path not found
or
Server object error 'ASP 0177 : 800a0046'
Server.CreateObject Failed

To give MachineA access to MachineB's shares, you need to fuss with the Anonymous User employed by IIS

(IUSR_MachineA, in this case).


First, on MachineA, you need to go to Internet Services Manager, right-click Default Web Site (or the application in

question).

Under the Directory Security tab, click 'Edit...' under 'Anonymous Access and Authentication Control.'

On Windows 2000, you will have to click 'Edit...' again under the Anonymous Access checkbox.
Uncheck "Allow IIS to control password" and enter a new password. When you click OK, you will be asked to

confirm.

Click OK, Apply, OK, and close out of ISM. If prompted to save console settings, say Yes / OK.

On MachineB, go into Local Users and Groups -- under Computer Management, add a user named IUSR_MachineA

with the same password as above.

In Windows Explorer on MachineB, right-click the share in question, hit Properties, and on the SECURITY tab Add...

the new local user, and give appropriate permissions. Hit Apply, Apply, OK.

Now run your ASP file from the original server and all should be fine!

If you are still getting the error, check that you haven't exceeded the connection limit of the machine with the share.

If it is Windows 2000 / XP Pro, only 10 anonymous users can be connected to it at a time. Consider using a server

operating system for hosting such a share, or hosting the share on the web server itself (that way IIS will be able to

grab the files locally, and interactive users will simply have to refer to the share on a different machine).

See Q207671 and Q189408 for more info.

Virtual Directories on other machines

See Q308150 for walk-throughs of configuring virtual directories from remote shares.
How do I page through a recordset?

22,583 requests - last updated Thursday, July 11, 2002

This article demonstrates how to page through a recordset WITHOUT using an explicit ADODB.Recordset object. We

have updated this sample to use a stored procedure in addition to straight ASP. There are two benefits to the stored

procedure version. One is that it is compiled after first execution, so much of the code only goes through overhead

once. More importantly, the stored procedure does its subset calculation within the database, so the entire recordset

is not sent over the wire. (We will be experimenting with getRows() soon.)

Let's assume we have the following table in SQL Server 2000 (don't worry, the non-stored procedure version will

work on a similarly structured table in Access):

CREATE TABLE [dbo].[CDs] (


[artist] [varchar] (255) NOT NULL DEFAULT '',
[title] [varchar] (255) NOT NULL DEFAULT '',
[cdn] [bit] NOT NULL DEFAULT 0
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[CDs] WITH NOCHECK ADD


CONSTRAINT [PK_CDs] PRIMARY KEY CLUSTERED
(
[artist],
[title]
) ON [PRIMARY]
GO

Let's also assume that someone went through the painstaking task of documenting their entire CD collection (which

I did, since I was sick of receiving duplicates as gifts <G>). So imagine around 1000 INSERT statements populating

the table above. If I were to publish this CD collection on the Internet (which I did, to accompany my Christmas

wish list <G>), I clearly wouldn't want people to have to go through all the CDs in one page. Perhaps they're only

interested in the U2 album missing from my U2 set. So, let's write some code to break this up at 50 CDs per page.

Plain ASP
This version uses ASP code to grab all the records, and only display the subset currently requested. It's a little

messy, but I hope the inline comments help to explain it...

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection_string>"

' tell me how many records you want per page


perPage = 50

' set up a query to get the count, and another to get the data
countSQL = "SELECT count(artist) FROM CDs"
dataSQL = "SELECT artist,title,cdn FROM CDs"

' start getting the data, first we need to know the count
' this will tell us how many pages there will be
set rs = conn.execute(countSQL)
counter = clng(rs(0))
rs.close: set rs = nothing

' determine if anything is returned. If there are records,


' figure out the pagecount and the total number of records.
if counter < 1 then
Response.Write "No records found."
Response.End
else

if counter = 1 then perpage = 1


totalRecords = counter
pageCount = clng(counter/perpage)
if counter/perPage > pageCount then
pageCount = pageCount + 1
end if

' what page are we on?


currentpage = clng(request.form("currentpage"))
if currentpage <= 1 then
currentpage = 1
elseif currentpage > pageCount then
currentpage = pagecount
end if
cp = currentPage
response.Write "<form method=post action=rsPage.asp "
response.Write "name=spform><b>Aaron's CDs</b> ... "
response.Write "count = " & counter & "</b> ... Jump to"
response.Write " page #<select name=currentpage "
response.Write "onchange='document.spform.submit();'>"
for i = 1 to pageCount
thisOpt = "<option value='" & i & "'>" & i
' highlight the current page!
response.Write replace(thisOpt," value='" & cp &_
"'", " value='" & cp & "' SELECTED")
next
Response.Write "</select></form><p>"
Response.Write "<p><table border=0 cellpadding=4 "
Response.Write "cellspacing=0 style='border:1px solid #999999'>"
end if

' set up the first record to move to, if applicable


if pageCount > 1 then
startrecord = (clng(currentpage-1) * perpage) + 1
end if

' let's get the data, move to the first record if necessary
' loop through, returning records until we meet perpage or EOF

set rs = conn.execute(dataSQL)
'if counter > perpage and startrecord > 1 then
rs.move(startrecord-1)
for x = 1 to perpage
if rs.eof then exit for
response.write "<tr valign=middle><td>"
artist = rs(0)
if artist <> prevArtist then
prevArtist = artist
if rs(2) then
artist = "<img src=leaf.gif><b>" & artist & "</b>"
else
artist = "<b>" & artist & "</b>"
end if
else
artist = " <span style='color:#999999'>" & artist & "</span>"
end if
response.write "<nobr>" & artist & "</nobr></td><td>"
response.write "<nobr>" & rs(1) & "</nobr></td></tr>"
rs.movenext
next
response.write "</table>"

' clean up
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

There is some extraneous formatting in there that you'll probably do differently, and there is some data missing

(e.g. style sheet parameters). Essentially, the above should work for any generic query. Obviously, if your SQL

statement will vary based on a search or other criteria, you will want to pass the countSQL and dataSQL statements

in a session variable instead of hard-coding them into the top of the page.

Stored Procedure

All right, so now you've seen the kids' example, let's move on to a bigger boy. Again, hopefully the inline comments

explain what is going on.


CREATE PROCEDURE dbo.usp_listCDs
@pagenumber INT = 1,
-- current page number; default is 1
@perpage INT = 50
-- let's have configurable page size
AS
BEGIN
SET NOCOUNT ON
DECLARE @pageNumbers INT
-- total number of pages

CREATE TABLE #TEMP


(
ID INT IDENTITY(1,1),
artist VARCHAR(255),
title VARCHAR(255),
cdn BIT
)
-- temp table to store subset of data

INSERT INTO #temp(artist,title,cdn)


SELECT artist,title,cdn FROM CDs ORDER BY artist,title
-- let's jam all the records into #temp

SELECT @pageNumbers = 1+((COUNT(id)-1)/@perpage) FROM #temp


-- weird formula to account for page 1
-- and those on the cusp (e.g. 50 records)

SELECT pageNumbers = @pageNumbers, count(id) FROM #temp


-- return number of pages, total count
-- to ASP page as first recordset

IF @pagenumber <= @pagenumbers


BEGIN
-- not the last page
SELECT artist,title,cdn
FROM #temp
WHERE id > (@perpage*(@pagenumber-1))
AND id <= (@perpage*@pagenumber)
ORDER BY artist,title
END
IF @pagenumber = @pagenumbers AND @pagenumber = 1
BEGIN
-- there's only one page
SELECT artist,title,cdn
FROM #temp
ORDER BY artist,title
END
IF @pagenumber = @pagenumbers AND @pagenumber > 1
BEGIN
-- last page
SELECT artist,title,cdn
FROM #temp
WHERE id > (@perpage*(@pagenumber-1))
ORDER BY artist,title
END
DROP TABLE #temp
END

So now, the ASP code to retrieve this information is similar to the previous example, ony a wee bit shorter:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection_string>"

prevArtist = ""

' what page are we on?


currentPage = clng(request.Form("currentpage"))
if currentPage = 0 then currentPage = 1
cp = currentPage

set rs = conn.execute("EXEC usp_listCDs " & currentPage)


numPages = rs(0)
numDiscs = rs(1)
Response.Write "<form method=post action=spPage.asp name=spform><b>"
Response.Write "Aaron's CDs</b> ... count = " & numDiscs & "</b> ..."
Response.Write " Jump to page #<select name=currentpage onchange='"
Response.Write "document.spform.submit();'>"
for i = 1 to numPages
thisOpt = "<option value='" & i & "'>" & i
' highlight the current page!
response.Write replace(thisOpt," value='" & cp & "'", " value='" &_
cp & "' SELECTED")
next
response.write "</select></form><p>"
response.write "<p><table border=0 cellpadding=4 cellspacing=0"
Response.Write " style='border:1px solid #999999'>"

set rs = rs.nextrecordset()

do while not rs.eof


response.write "<tr valign=middle><td>"
artist = rs(0)
if artist <> prevArtist then
prevArtist = artist
if rs(2) then
artist = "<img src=leaf.gif><b>" & artist & "</b>"
else
artist = "<b>" & artist & "</b>"
end if
else
artist = " <span style='color:#999999'>" & artist & "</span>"
end if
response.write "<nobr>" & artist & "</nobr></td><td>"
response.write "<nobr>" & rs(1) & "</nobr></td></tr>"
rs.movenext
loop
response.write "</table>"

' clean up
rs.close: set rs = nothing
conn.close: set conn = nothing
%>
Comparing Methods

Sometimes it takes a lot to convince people that a stored procedure is faster than ASP recordset processing, and

often it takes even more to show that using a temporary table can sometimes be worth the trouble. Finally, it is

impossible to convince people that there are too many variables to say that one method will ALWYAS be faster than

another. To show these facts, I have posted these examples online, with timer() code, so you can compare for

yourself. Run each page, apples to apples, and scroll down to compare execution times. You will find, like I did, that

while the stored procedure method often outran the ASP code, the reverse was occasionally true as well. This could

be due to various things, including network traffic (at both server and client ends), other database activity, etc.

Recordset paging

Stored procedure paging

External Information

Here are two articles at 15seconds.com that describe paging:

http://www.15seconds.com/Issue/010308.htm

http://www.15seconds.com/issue/010607.htm
How do I get the ID number of a just-inserted record?

19,329 requests - last updated Sunday, July 21, 2002

SQL Server

With SQL Server 2000, there are a couple of new functions that are better than @@IDENTITY. Both of these

functions are not global to the connection, which is an important weak point of @@IDENTITY. After doing an

insert, you can call:

PRINT IDENT_CURRENT('table')

This will give the most recent IDENTITY value for 'table' - regardless of whether you created it or not (this

overrides the connection limitation of @@IDENTITY -- which can be useful).

Another thing you can do is:

PRINT SCOPE_IDENTITY()

This will give the IDENTITY value last created within the current stored procedure, trigger, etc.

If you are not using SQL Server 2000, the best way with SQL Server is to use a single stored procedure that

handles both the INSERT and the IDENTITY retrieval using @@IDENTITY. Here is sample code for the stored

procedure:
CREATE PROCEDURE myProc
@param1 INT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO someTable
(
intField
)
VALUES
(
@param1
)
SET NOCOUNT OFF
SELECT NEWID = @@IDENTITY
END

And you would call this from ASP as follows:

<%
fakeValue = 5
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<conn string>"
set rs = conn.execute("EXEC myProc @param1=" & fakeValue)
response.write "New ID was " & rs(0)
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

If you are using SQL Server 2000, simply change the line in the stored procedure from ...

SELECT NEWID = @@IDENTITY

... to ...
SELECT NEWID = SCOPE_IDENTITY()

One of the problems with the global nature of @@IDENTITY is that if you do an INSERT, and that table has a

TRIGGER ON INSERT which then, in turn, inserts into another table with an IDENTITY field, @@IDENTITY is

populated with the second table's value. So, if you are stuck using SQL Server 7.0 and need a workaround to

retrieving the @@IDENTITY value because you have a trigger that also inserts into another IDENTITY-bound

table, you're in luck. You can add this code to the first line of the trigger, but you will have to update all of

your application and stored procedure code to deal with this new SELECT:

CREATE TRIGGER triggerInsert_tablename ON tablename FOR INSERT AS


BEGIN
SELECT @@IDENTITY
-- rest of trigger's logic...
END

Access

Jet/OLEDB provider now supports @@IDENTITY! See Q232144for more info.

With Access, you should be able to get away with something like this:

<%
fakeValue = 5
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<conn string>"
conn.execute "Insert into someTable(intField) values(" & fakeValue & ")"
set rs = conn.execute("select MAX(ID) from someTable")
response.write "New ID was " & rs(0)
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

Now I say "should" because it is (though very obscurely) possible for two people to "cross" inserts, and
receive the wrong autonumber value back. To be frank, if there is a possibility of two or more people

simultaneously adding records, you should already be considering SQL Server (see Article #2182). However,

if you're stuck with Access and need more security that this won't happen, you can use a Recordset object

with an adOpenKeyset cursor (this is one of those rare scenarios where a Recordset object actually makes

more sense than a direct T-SQL statement):

<%
fakeValue = 5
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<conn string>"
set rs = Server.CreateObject("ADODB.Recordset")
rs.open "select [intField] from someTable where 1=0", conn, 1, 3
rs.AddNew
rs("intField") = fakeValue
rs.update
response.write "New ID was " & rs("id")
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

You can also look at Manohar's excellent articles at Active Server Corner:

http://www.kamath.com/tutorials/tut004_autonum.asp

http://www.kamath.com/tutorials/tut007_identity.asp

And check out MS' KB article:

http://support.microsoft.com/?kbid=221931
How do I deal with an apostrophe (') in a SQL statement?

18,638 requests - last updated Wednesday, March 6, 2002

This has got to be the most-often asked question in all ASP forums. The apostrophe is an illegal character in SQL

because it is interpreted as a string delimiter. To allow a ' mark to be inserted into a database, simply double-up all

occurences of the ' mark:

<%
criteria = replace(criteria,"'","''")
%>

Inside the parentheses, for clarity, that's criteria, comma, quote, apostrophe, quote, comma, quote, apostrophe,

apostrophe, quote (without the spaces). So to generate SQL queries:

<%
mycrit = replace(mycrit,"'","''")
response.write("INSERT table VALUES('" & mycrit & "')<p>")
response.write("SELECT column FROM table WHERE column LIKE '%" & mycrit & "%'<p>")
%>

In JScript, you could use the Replace() method also, however it behaves differently. Each call to .Replace() only

affects the *first* instance it comes across. You can use RegExp to remind JScript to replace globally:

<script language=jscript runat=server>


var myCrit = "bob's bait and tackle";
var q = /\'/g; // regexp apostrophe, global
myCrit = myCrit.replace(q, "''");
Response.Write("INSERT table VALUES('" + myCrit + "')<P>");
Response.Write("SELECT column FROM table WHERE column LIKE '%" + myCrit + "%'<P>");
</script>
What is wrong with 'SELECT *'?

16,962 requests - last updated Wednesday, August 14, 2002

SELECT * is inefficient, particularly when you are only using a few of the columns in the table. This is because it

actually makes TWO queries to the database: before it runs your query, it has to query the system tables to

determine the name and datatypes of the columns. It is much more efficient to NAME your columns in the SQL

query, and this will also help in having your column names right there... so you don't have to keep flipping back and

forth between ASP page and database. In addition, this will prevent ambiguous column names in your resultset (in

the case where both are all tables in the JOIN statement have columns with the same name).

And finally, here's another reason to avoid SELECT * : Memo/Text columns, as well as columns containing BLOB

data. Microsoft recommends to put BLOB/text columns at the end of the SELECT statement, in order of appearance

in the table. This is also applicable to VARCHAR columns in SQL Server with a length greater than 255 characters.

For more information:

Article #2188

Q175239
What are reserved Access, ODBC and T-SQL keywords?

16,125 requests - last updated Monday, July 8, 2002

The following three tables document reserved words that should not be used as names for columns, tables, aliases,

or other user-defined objects. If you are getting syntax errors of any kind, these are often due to using one of these

reserved words.

Note that 'password' causes 0x80040E14 errors when used in an Access table, though it is not referenced directly in

any of the 'official' reserved words documentation (from which the following was adapted)...

Access | ODBC | Transact-SQL

ADD ALL ALPHANUMERIC ALTER AND

ANY APPLICATION AS ASC ASSISTANT

AUTOINCREMENT AVG BETWEEN BINARY BIT

BOOLEAN BY BYTE CHAR CHARACTER

COLUMN COMPACTDATABASE CONSTRAINT CONTAINER COUNT

COUNTER CREATE CREATEDATABASE CREATEFIELD CREATEGROUP

CREATEINDEX CREATEOBJECT CREATEPROPERTY CREATERELATION CREATETABLEDEF

CREATEUSER CREATEWORKSPACE CURRENCY CURRENTUSER DATABASE

DATE DATETIME DELETE DESC DESCRIPTION

DISALLOW DISTINCT DISTINCTROW DOCUMENT DOUBLE

DROP ECHO ELSE END EQV

ERROR EXISTS EXIT FALSE FIELD

FIELDS FILLCACHE FLOAT FLOAT4 FLOAT8


FOREIGN FORM FORMS FROM FULL

FUNCTION GENERAL GETOBJECT GETOPTION GOTOPAGE

GROUP GROUP BY GUID HAVING IDLE

IEEEDOUBLE IEEESINGLE IF IGNORE IMP

IN INDEX INDEXES INNER INSERT

INSERTTEXT INT INTEGER INTEGER1 INTEGER2

INTEGER4 INTO IS JOIN KEY

LEFT LEVEL LIKE LOGICAL LOGICAL1

LONG LONGBINARY LONGTEXT MATCH MAX

MIN MOD MEMO MONEY MOVE

NAME NEWPASSWORD NO NOT NULL

NUMBER NUMERIC OBJECT OLEOBJECT OFF

ON OPENRECORDSET OPTION OR ORDER

OUTER OWNERACCESS PARAMETER PARAMETERS PARTIAL

PERCENT PIVOT PRIMARY PROCEDURE PROPERTY

QUIT REAL RECALC RECORDSET REFERENCES

REFRESH REFRESHLINK REGISTERDATABASE RELATION REPAINT

REPAIRDATABASE REPORTS REQUERY RIGHT SCREEN

SECTION SELECT SET SETFOCUS SETOPTION

SHORT SINGLE SMALLINT SOME SQL

STDEV STDEVP STRING SUM TABLE

TABLEDEF TABLEDEFS TABLEID TEXT TIME


TIMESTAMP TOP TRANSFORM TRUE UNION

UNIQUE UPDATE VALUE VALUES VAR

VARP VARBINARY VARCHAR WHERE WITH

WORKSPACE XOR YES YESNO

Access | ODBC | Transact-SQL

ABSOLUTE ACTION ADA ADD ADD

ALL ALLOCATE ALTER AND ANY

ARE AS ASC ASSERTION AT

AUTHORIZATION AVG BEGIN BETWEEN BIT

BIT_LENGTH BOTH BY CASCADE CASCADED

CASE CAST CATALOG CHAR CHAR_LENGTH

CHARACTER CHARACTER_LENGTH CHECK CLOSE COALESCE

COLLATE COLLATION COLUMN COMMIT CONNECT

CONNECTION CONSTRAINT CONSTRAINTS CONTINUE CONVERT

CORRESPONDING COUNT CREATE CROSS CURRENT

CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR

DATE DAY DEALLOCATE DEC DECIMAL

DECLARE DEFAULT DEFERRABLE DEFERRED DELETE

DESC DESCRIBE DESCRIPTOR DIAGNOSTICS DISCONNECT

DISTINCT DOMAIN DOUBLE DROP ELSE


END END-EXEC ESCAPE EXCEPT EXCEPTION

EXEC EXECUTE EXISTS EXIT EXTERNAL

EXTRACT FALSE FETCH FIRST FLOAT

FOR FOREIGN FORTRAN FOUND FROM

FULL GET GLOBAL GO GOTO

GRANT GROUP HAVING HOUR IDENTITY

IMMEDIATE IN INCLUDE INDEX INDICATOR

INITIALLY INNER INPUT INSENSITIVE INSERT

INT INTEGER INTERSECT INTERVAL INTO

IS ISOLATION JOIN KEY LANGUAGE

LAST LEADING LEFT LEVEL LIKE

LOCAL LOWER MATCH MAX MIN

MINUTE MODULE MONTH NAMES NATIONAL

NATURAL NCHAR NEXT NO NONE

NOT NULL NULLIF NUMERIC OCTET_LENGTH

OF ON ONLY OPEN OPTION

OR ORDER OUTER OUTPUT OVERLAPS

PAD PARTIAL PASCAL POSITION PRECISION

PREPARE PRESERVE PRIMARY PRIMARY PRIOR

PRIVILEGES PROCEDURE PUBLIC READ REAL

REFERENCES RELATIVE RESTRICT REVOKE RIGHT

ROLLBACK ROWS SCHEMA SCROLL SECOND


SECTION SELECT SESSION SESSION_USER SET

SIZE SMALLINT SOME SPACE SQL

SQLCA SQLCODE SQLERROR SQLSTATE SQLWARNING

SUBSTRING SUM SYSTEM_USER TABLE TEMPORARY

THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE

TO TRAILING TRANSACTION TRANSLATE TRANSLATION

TRIM TRUE UNION UNIQUE UNKNOWN

UPDATE UPPER USAGE USER USING

VALUE VALUES VARCHAR VARYING VIEW

WHEN WHENEVER WHERE WITH WORK

WRITE YEAR ZONE

Access | ODBC | Transact-SQL

ADD ALL ALTER AND ANY

AS ASC AUTHORIZATION AVG BACKUP

BEGIN BETWEEN BREAK BROWSE BULK

BY CASCADE CASE CHECK CHECKPOINT

CLOSE CLUSTERED COALESCE COLUMN COMMIT

COMMITTED COMPUTE CONFIRM CONSTRAINT CONTAINS

CONTAINSTABLE CONTINUE CONTROLROW CONVERT COUNT

CREATE CROSS CURRENT CURRENT_DATE CURRENT_TIME


CURRENT_TIMESTAMP CURRENT_USER CURSOR DATABASE DBCC

DEALLOCATE DECLARE DEFAULT DELETE DENY

DESC DISK DISTINCT DISTRIBUTED DOUBLE

DROP DUMMY DUMP ELSE END

ERRLVL ERROREXIT ESCAPE EXCEPT EXEC

EXECUTE EXISTS EXIT FETCH FILE

FILLFACTOR FLOPPY FOR FOREIGN FREETEXT

FREETEXTTABLE FROM FULL GOTO GRANT

GROUP HAVING HOLDLOCK IDENTITY IDENTITY_INSERT

IDENTITYCOL IF IN INDEX INNER

INSERT INTERSECT INTO IS ISOLATION

JOIN KEY KILL LEFT LEVEL

LIKE LINENO LOAD MAX MIN

MIRROREXIT NATIONAL NOCHECK NONCLUSTERED NOT

NULL NULLIF OF OFF OFFSETS

ON ONCE ONLY OPEN OPENDATASOURCE

OPENQUERY OPENROWSET OPTION OR ORDER

OUTER OVER PERCENT PERM PERMANENT

PIPE PLAN PRECISION PREPARE PRIMARY

PRINT PRIVILEGES PROC PROCEDURE PROCESSEXIT

PUBLIC RAISERROR READ READTEXT RECONFIGURE

REFERENCES REPEATABLE REPLICATION RESTORE RESTRICT


RETURN REVOKE RIGHT ROLLBACK ROWCOUNT

ROWGUIDCOL RULE SAVE SCHEMA SELECT

SERIALIZABLE SESSION_USER SET SETUSER SHUTDOWN

SOME STATISTICS SUM SYSTEM_USER TABLE

TAPE TEMP TEMPORARY TEXTSIZE THEN

TO TOP TRAN TRANSACTION TRIGGER

TRUNCATE TSEQUAL UNCOMMITTED UNION UNIQUE

UPDATE UPDATETEXT USE USER VALUES

VARYING VIEW WAITFOR WHEN WHERE

WHILE WITH WORK WRITETEXT


Which is better, rs(0) or rs("fieldname")?

12,544 requests - last updated Monday, August 14, 2000

It has been proven time and time again that accessing recordset elements by index number is several times faster

than by name. A string lookup is much more expensive, resource-wide, than an integer lookup (this is true in

virtually all computer-based applications). Now, it might be difficult to keep track of name->ordinal pairs when

dealing with larger resultsets. There is definitely a trade-off for maintainability... but as long as you control the order

of the elements (by avoiding SELECT *, which you shouldn't be using anyway), you should be able to stay on top of

it. To make the transition easier, you should insert a little key for yourself when generating a resultset, such as the

following:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC sp_getRecords")
if not rs.eof then
do while not rs.eof

id = rs(0) ' SELECT ID,


fname = rs(1) ' fname,
lname = rs(2) ' lname FROM table

' ... process results using local vars

rs.movenext
loop
end if
' ... clean up etc.
%>

The only danger here is when somebody changes the order of fields in the SELECT list for the SQL statement or

stored procedure.
How do I solve 'Operation must use an updateable query' errors?

10,030 requests - last updated Sunday, August 11, 2002

WHen using Microsoft Access, you may have come across one of the following errors:

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Microsoft Access 97 Driver]
Operation must use an updateable query.

or

Microsoft JET Database Engine (0x80004005)


Operation must use an updateable query.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e09'


[Microsoft][ODBC Microsoft Access 97 Driver]
Record(s) can't be read; no read permission on '<table>'.

or

Microsoft JET Database Engine error '80040e09'


Cannot update. Database or object is read-only.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e09'


[Microsoft][ODBC Microsoft Access 97 Driver] You don't have the necessary
permissions to use the '<table>' object. Have your system administrator or
the person who created this object establish the appropriate permissions for
you.

This is almost always a permissions issue. Be sure that the MDB file is in a folder where IUSR has read/write access

(because IUSR_<machine_name> needs to create an .LDB file when modifying the database). Also be sure that the
MDB file itself isn't marked as read-only, and that you don't have the MDB file open (particularly in exclusive mode)

while trying to reach the DB from ASP. See Article #2326 and Q175168 for more information.

Another possible reason is that the column actually can't be updated, for example because of a constraint

relationship with another table.

Of course this error could also happen if you're storing your Access database on a floppy disk that has write

protection enabled (or that is full). Please don't do this; using Access is inefficient enough. You don't want to add

floppy seek time to your performance issues.


Why should I avoid NULLs in my database?

9,319 requests - last updated Saturday, January 6, 2001

Joe Celko said it best: "NULLs confuse people..." (SQL For Smarties, ISBN 1558605762). McGoveran and Date add:

"NULLs...are far more trouble than they are worth and should be avoided; they display very strange and inconsistent

behavior and can be a rich source of error and confusion." (Guide to Sybase and SQL Server, ISBN 020155710X).

My sentiments exactly. Of course, I don't expect to convince you by flashing a few quotes from very reputable

authors in front of you. Let's talk for a minute about what exactly NULLs do that cause this type of reaction. The first

problem is that the definition of NULL is "unknown." So, one problem is determining whether one value is (not)

equal to another value, when one or both values are NULL. This trickles down to many problems for a database

engine and any associated applications. The following list details some of those problems:

■ they are interpreted differently depending on compatibility level and ANSI settings;

For example, let's consider two values, x and y, that are both NULL. Since the definition of NULL is

unknown, then you can't say x = y. However, with the ANSI setting ANSI_NULLs, this can be

different. When this setting is FALSE, x = y ... however, when TRUE, x <> y. Confusing, no?

■ the storage engine has to do extra processing for each row to determine if the NULLable column is in fact

NULL -- this extra bit field affects storage and indexing, and obviously has performance implications for

general queries;

■ they produce weird results when using calculations, comparisons, sorting and grouping;

■ they create problems with aggregates and joins, such as different answers for COUNT(*) vs.

COUNT(fieldname);

■ they produce unpredictable results in statistics computations, particularly WITH ROLLUP and WITH CUBE;

■ applications must add extra logic to handle inserting and retrieving results, which may or may not include

NULL values;

■ they cause unpredictable results with NOT EXISTS and NOT IN subqueries (working backwards, SQL

determines that NULL columns belong or do not belong to the result set, usually for the wrong reasons);

■ no language that supports embedded SQL has native support for NULL SQL values.

Here are some more specific examples:


USE PUBS
SELECT COUNT(state) FROM publishers
SELECT COUNT(pub_name) FROM publishers

Why the difference in count results? You would *think* that the count would give a rowcount regardless of the

contents of the column. It is often recommended that "*" be avoided because it is inefficient (causing an extra call

to the syscolumns table) -- however in this case, if you allow NULL values in your fields, you run the risk of basing

your count on a field which contains NULLs... leading to an inaccurate count. So, because of your design, you're

almost forced to use an inefficient method to obtain count (whereas, if you didn't allow NULLs, a default value --

that could still act as a flag -- WOULD get counted).

Here is another more involved example. Let's say you're running a stats program, and someone has to enter things

manually. What if they don't know the adid and/or siteid when they enter the data, and you're doing rollups against

it? If you haven't used it before, WITH ROLLUP groups by your GROUP BY fields, then adds summary rows. It adds

flags to each column when you're at a summary row, so that you can identify WHICH summary row it is. Guess what

the flag is? NULL. So, try out this code:

USE pubs
CREATE TABLE fakeStats
(
id INT IDENTITY NOT NULL,
adid INT,
siteid INT,
hitcount INT
)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(1,1,40)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(1,1,20)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(1,2,30)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(1,3,40)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(2,1,40)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(2,2,60)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(2,2,20)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(2,2,30)
INSERT INTO fakeStats(adid,siteid,hitcount) VALUES(2,3,10)

SELECT adid,siteid,sum(hitcount) FROM fakeStats


GROUP BY adid,siteid WITH ROLLUP

You'll see that the results clearly identify the summary rows with NULL flags. Unfortunately, if you have NULLs *in

the data*, this becomes very difficult to process automatically. For example, run this now:

UPDATE fakeStats SET adid=NULL where id=4


UPDATE fakeStats SET siteid=NULL where id=6

SELECT adid,siteid,sum(hitcount) FROM fakeStats


GROUP BY adid,siteid WITH ROLLUP

See the difference? Which rows are the summary rows now? Easy enough to figure out, if you have a small result

set and time to straighten out the mess. However, if you've got a system that automatically (or on-demand) creates

reports against a data warehouse, I think you can see how NULL values will put up some roadblocks.

The only datatype where NULLs are unavoidable in certain scenarios are DATETIME columns -- some dates are

clearly unknown. But for all the reasons cited above, my opinion is that a flag, non-sensical date is better. In either

case, you have to use logic to omit flagged records from result sets, but you can avoid all the other problems

associated with NULLs.

So, my suggestion is to always use a DEFAULT value, and declare all columns explicitly as NOT NULL. The default in

DDL for column creation, at least in SQL Server, is NULL. One thing you should keep in mind is that, whether you

decide to use NULL or NOT NULL, is that this can change per SQL Server (and can even change between DDL

executions). So, you should always explicitly define NULL or NOT NULL for every column in your CREATE TABLE

statements, to avoid confusion and unpredictable results.


Should I use ADOVBS.inc for declaring constants?

9,305 requests - last updated Friday, December 14, 2001

Sadly, VBScript does not understand ADO constants, such as AdLockReadOnly, the way VB does. Luckily (or

unluckily) for the developer, the immense number of constants for use with ADO is compiled in a file called

ADOVBS.inc. When starting out with database development, everyone is told to use the constant names (as opposed

to their integer counterparts), and to include ADOVBS.inc. This recommendation is given by many people, even if

you are only using 2 or 3 of the 393 constants that are listed in ADOVBS.inc.

These 393 constants result in an overall raw read size of 14,639 bytes. Never mind the amount of memory allocated

to holding all of those variables, most of which you have no intention of using. On a small, single-user app, you

won't see a difference. However, when you have 300 people on your site at once, every single user has to load that

15kb file, and every user has 393 extra page-level variables in memory... this can really add up.

This is why my suggestion will always be to either:

1. Write your own adovbs.inc file, with only the handful of constants you need;

2. Only define constants as you need them;

3. Place the relevant constant/value pairs in a comment, and use the integer equivalent in the actual code; or

4. Use <!--METADATA...--> references in global.asa:

<!--METADATA
TYPE="TypeLib"
NAME="Microsoft ActiveX Data Objects 2.7 Library"
UUID="{EF53050B-882E-4776-B643-EDA472E8E3F2}"
VERSION="2.7"-->

Here is the code for version 2.6:


<!--METADATA
TYPE="TypeLib"
NAME="Microsoft ActiveX Data Objects 2.6 Library"
UUID="{00000206-0000-0010-8000-00AA006D2EA4}"
VERSION="2.6"-->

And if you're stuck with version 2.5, use the following:

<!--METADATA
TYPE="TypeLib"
NAME="Microsoft ActiveX Data Objects 2.5 Library"
UUID="{00000205-0000-0010-8000-00AA006D2EA4}"
VERSION="2.5"-->

If you're using a version of MDAC prior to 2.5, it's time to upgrade to the most recent version.

Note that this same argument holds true if you are using JScript and its equivalent include file, ADOJAVAS.INC.
What is wrong with 'LIKE *'?

9,282 requests - last updated Tuesday, January 23, 2001

ADO uses % for wildcard translation, not *. Therefore, if you use * as a wildcard, the query will actually be

searching for records that have a literal * in the field data. Also, you should use _ instead of ?. Access natively

supports % and _ (in addition to * and ?), so I recommend using those everywhere instead of being forced to worry

about statement conversion when web-ifying anything.


How do I retrieve a random record?

8,840 requests - last updated Sunday, November 4, 2001

Assuming there is a unique identifier for each row, and that there is at least one record in the table, retrieving a

random record can be quite easy. This method will work in SQL Server 7.0 and above (running on Windows 2000),

but this could be a performance problem on larger tables / resultsets:

<%
SELECT TOP 1 someColumn
FROM someTable
ORDER BY NEWID()
%>

If you are not running on Windows 2000, then the following code will work if your table has a unique identifier (e.g.

IDENTITY) column:

<%
SELECT TOP 1 someColumn
FROM someTable
ORDER BY RAND((1000*IDColumn)*DATEPART(millisecond, GETDATE()))
%>

Note that both of these methods also allow you to select 10 or 50 or 5000 random records, simply by altering the

TOP parameter.

SQL Server has a few other tricks up its sleeve as well...

Now, if you're stuck using Access or SQL Server 6.5, you may have to use some logic on the application end to deal

with this. Here are the ordered steps your code must take:

1. retrieve a recordcount, so you know how many records you need to "randomize"

2. place the ID numbers in an array, one for each "hit" in your recordcount

3. run the recordcount through a randomizer, and figure out which ID you're going to pick

4. create a select statement matching the random number to its ID in the array
Here is some code that will do this:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<conn string>"

' ***** (step 1) *****

set rs = conn.execute("SELECT COUNT(IDColumn) FROM someTable")


rCount = rs(0)

' ***** (step 2) *****

set rs = conn.execute("SELECT IDColumn FROM someTable")


cnt = 1
dim RRs
redim RRs(rCount)
do while not rs.eof
RRs(cnt) = rs(0)
cnt = cnt + 1
rs.movenext
loop

' ***** (step 3) *****

randomize
currentRR = cLng(rnd*rCount+0.5)
ID = RRs(currentRR)

' ***** (step 4) *****

sql = "SELECT someColumn FROM someTable WHERE id=" & ID


set rs = conn.execute(sql)
response.write "ID # " & ID & " = " & rs(0)
' ...
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

With SQL Server, this would be much faster with a stored procedure... I just wanted to provide syntax here that

demonstrates the concept, and will work on either Access or SQL Server.
How do I upsize from Access to SQL Server?

6,644 requests - last updated Wednesday, January 9, 2002

Access is not truly meant for concurrent use. Microsoft made it easy to use Access over the web so that small

companies who couldn't afford (or were intimidated by) SQL Server would have some database platform to use for

their LANs (great way to sell more copies of the higher-end Office package, as well). Unfortunately for Internet

developers, Access does not handle more than a handful of simultaneous users very well at all. So if you've got a

family site that only your friends and such are looking at, Access is probably fine. For anything more than that, you'll

probably want to use SQL Server.

Here is a KB article, two tools and a white paper that will help you upsize your Access 97 or 2000 database to SQL

Server (6.5 or greater):

Q237980 How to Convert an Access Database to SQL Server

For Access 97:

http://office.microsoft.com/downloads/9798/aut97.aspx

For Access 2000 / XP:

http://office.microsoft.com/downloads/2000/Accsql.aspx

(white paper available from Q294407)

If you know you are converting from Access 2000 to SQL Server 2000, you may want to pick up the SQL Server

2000 Resource Kit. It has an updated version of the upsizing wizard that fixes some problems, and makes Access

2000 projects (.adp) much more fluent in the changes to SQL Server 2000.

There are also some 3rd party products out there that help the migration. One is SSW Upsizing Pro and another is

DataConvert.

Finally, here are some Knowledge Base articles you may want to glance over before proceeding:

Q288300 "Microsoft Access Can't Find the Wizard..." Error [...]

Q282380 Bad File Name or Number [...]


Q285829 How to Use DTS [...]

Q281950 Database Unexpectedly Upsized [...]

Q272384 "Overflow" Error Message When You Try [...]

Q269824 Incompatibility Issues Between Access 2000 Projects [...]

Q165827 "Overflow" or "Division by Zero" Error Upsizing Table

Q153034 Table Is Not Exported Using the Upsizing Tools

Now that you see how much work is involved, and that there are plenty of potential issues, wouldn't it make sense

to just prototype your application using SQL Server in the first place?
What do I need to know about the differences between Access and SQL Server?

6,442 requests - last updated Monday, July 1, 2002

This article will try to explain some of the differences between Access and SQL Server. It is not an exhaustive list,

and in no means should be considered an ultimate authority. If you have anything to add or correct, please let us

know...

DATA TYPES

Here is a list of data types in each environment, and how they are different. Some datatypes from SQL Server were

left out (e.g. SQL_VARIANT, TABLE).

Access SQL Server SQL Server Definition

Yes/No BIT (Integer: 0 or 1)

Number (Byte) TINYINT (Positive Integer 0 -> 255)

Number (Integer) SMALLINT (Signed Integer -32,768 -> 32,767)

Number (Long Integer) INT (Signed Integer -(2^31) -> (2^31)-1)

(no equivalent) BIGINT (Signed Integer -(2^63) -> (2^63)-1)

Number (Single) REAL (Floating precision -3.40E + 38 -> 3.40E + 38)

Number (Double) FLOAT (Floating precision -3.40E + 38 -> 3.40E + 38)

Currency MONEY (4 decimal places, -(2^63)/10000 -> ((2^63)-1)/10000)

Currency SMALLMONEY (4 decimal places, -(2^31)/10000 -> ((2^31)-1)/10000)

Hyperlink (no equivalent - use VARCHAR())

Decimal DECIMAL (Fixed precision -10^38 + 1 -> 10^38 - 1)

Numeric NUMERIC (Fixed precision -10^38 + 1 -> 10^38 - 1)

Date/Time DATETIME (Date+Time 1/1/1753->12/31/9999, accuracy of 3.33 ms)


Date/Time SMALLDATETIME (Date+Time 1/1/1900->6/6/2079, accuracy of one minute)

Autonumber IDENTITY (INT with IDENTITY property)

Text(n) CHAR(n) (Fixed-length non-Unicode string to 8,000 characters)

Text(n) NCHAR(n) (Fixed-length Unicode string to 4,000 characters)

Text(n) VARCHAR(n) (Variable-length non-Unicode string to 8,000 characters)

Text(n) NVARCHAR(n) (Variable-length Unicode string to 4,000 characters)

(Variable-length non-Unicode string to 2,147,483,647


Memo TEXT
characters)

OLE Object BINARY (Fixed-length binary data up to 8,000 characters)

OLE Object VARBINARY (Variable-length binary data up to 8,000 characters)

OLE Object IMAGE (Variable-length binary data up to 2,147,483,647 characters)

Some notes on usage of data types:

Switching from Yes/No to BIT

In Access, you could use integers or TRUE/FALSE keywords to determine the value of the field. In SQL

Server, and especially during migration, you should use integer values only. So here are some sample

queries; note that the SQL Server queries will work Access as well.

-- DETERMINING TRUE

-- Access:
[...] WHERE ynColumn = TRUE
[...] WHERE ynColumn = -1

-- SQL Server:
[...] WHERE ynColumn <> 0

------------------------------
-- DETERMINING FALSE

-- Access:
[...] WHERE ynColumn = FALSE
[...] WHERE ynColumn = 0

-- SQL Server:
[...] WHERE ynColumn = 0

Switching from Currency to MONEY

You will no longer be able to use cute VBA functions like FORMAT to add dollar signs, thousand separators,

and decimal places to your numbers. In fact, in Access, some of this data is actually stored along with the

value. With SQL Server, this extraneous data is not stored, reducing disk space and making calculations

more efficient. While you can apply this formatting in SQL Server, as explained in Article #2188, it's messy --

and better handled, IMHO, by the client application. In ASP, you can use built-in functions like

FormatCurrency to apply proper formatting to your money values.

Switching from Hyperlink to VARCHAR()

Like Currency, Access uses internal formatting to make the values stored in the application clickable. This is

partly because Access is a client application, and this feature makes it easier to use. However, when you're

not physically in the application, you may not want the URL to be clickable (it may just be a display value, or

you may want to wrap alternate text -- or an image -- inside the <a href> tag). In SQL Server, use a

VARCHAR column (likely 1024 or greater, depending on the need) and apply <a href> tags to it in the client

application. Don't expect the database to maintain HTML for you... this only increases storage size, and hurts

performance of searches against that column.

Switching from Date/Time to DATETIME

When passing dates into Access from ASP or an application, you use pound signs (#) for surrounding dates.

SQL Server, on the other hand, uses apostrophes ('). So the following query conversion would be required:
-- Access:
[...] WHERE dtColumn >= #11/05/2001#

-- SQL Server:
[...] WHERE dtColumn >= '11/05/2001'

In addition, Access allows you to store date and time alone. SQL Server does not allow this (see Article

#2206for more info). To see if a date equals 11/05/2001 in SQL Server, you would have to convert the

stored value (which includes time) to a 10-character date. Here is how a typical query would have to

change:

-- Access:
[...] WHERE dtColumn = #11/05/2001#

-- SQL Server:
[...] WHERE CONVERT(CHAR(10), dtColumn, 101) = '11/05/2001'

If you want to retrieve the current date and time, the syntax is slightly different:

-- Access:
SELECT Date() & " " & Time()

-- SQL Server:
SELECT GETDATE()
SELECT CURRENT_TIMESTAMP

To get tomorrow's date, here is how your queries would look:

-- Access:
SELECT DateAdd("d",1,date())

-- SQL Server:
SELECT CONVERT(CHAR(10), DATEADD(day, 1, GETDATE()), 101)
To get the date and time 24 hours from now:

-- Access:
SELECT cstr(DateAdd("d",1,date())) & " " & cstr(time())

-- SQL Server:
SELECT DATEADD(day, 1, GETDATE())

To get the first day of the current month:

-- Access:
SELECT DateAdd("d",1-day(date()),date())

-- SQL Server:
SELECT CONVERT(CHAR(10),GETDATE()+1-DAY(GETDATE()),101)

To get the number of days in the current month:

-- Access:
SELECT DAY(DATEADD("m", 1, 1-DAY(date()) & date())-1)

-- SQL Server:
SELECT DAY(DATEADD(MONTH, 1, 1-DAY(GETDATE())+GETDATE())-1)

To get the current millisecond:

-- Access:
SELECT "Pick a number between 1 and 1000"

-- SQL Server:
SELECT DATEPART(millisecond, GETDATE())

To get the current weekday:


-- Access:
SELECT weekdayname(weekday(date()))

-- SQL Server:
SELECT DATENAME(WEEKDAY, GETDATE())

Switching from Autonumber to IDENTITY

Not much difference here, except for how you define the column in DDL (CREATE TABLE) statements:

-- Access:
CREATE TABLE tablename (id AUTOINCREMENT)

-- SQL Server:
CREATE TABLE tablename (id INT IDENTITY)

Handling Strings

There are many changes with string handling you will have to take into account when moving from Access to

SQL Server. For one, you can no longer use double-quotes (") as string delimiters and ampersands (&) for

string concatenation. So, a query to build a string would have to change as follows:

-- Access:
SELECT "Foo-" & barColumn FROM TABLE

-- SQL Server:
SELECT 'Foo-' + barColumn FROM TABLE

(Yes, you can enable double-quote characters as string delimiters, but this requires enabling

QUOTED_IDENTIFIERS at each batch, which impacts many other things and is not guaranteed to be forward

compatible.)

Built-in CHR() constants in Access change slightly in SQL Server. The CHR() function is now spelled slightly
differently. So, to return a carriage return + linefeed pair:

-- Access:
SELECT CHR(13) & CHR(10)

-- SQL Server:
SELECT CHAR(13) + CHAR(10)

This one is confusing for many people because the CHAR keyword doubles as a function and a datatype

definition.

String Functions

There are many VBA-based functions in Access which are used to manipulate strings. Some of these

functions are still supported in SQL Server, and aside from quotes and concatenation, code will port without

difficulty. Others will take a bit more work. Here is a table of the functions, and they will be followed by

examples. Some functions are not supported on TEXT columns; these differences are described in Article

#2061.

Access SQL Server TEXT Equivalent

CINT(), CLNG() CAST() CAST(SUBSTRING())

INSTR() CHARINDEX() CHARINDEX()

ISDATE() ISDATE() ISDATE(SUBSTRING())

ISNULL() ISNULL() ISNULL()

ISNUMERIC() ISNUMERIC() ISNUMERIC(SUBSTRING())

LEFT() LEFT() SUBSTRING()

LEN() LEN() DATALENGTH()

LCASE() LOWER() LOWER(SUBSTRING())


LTRIM() LTRIM() LTRIM(SUBSTRING())

REPLACE() REPLACE() STUFF()

RIGHT() RIGHT() SUBSTRING()

RTRIM() RTRIM() RTRIM(SUBSTRING())

CSTR() STR() STR(SUBSTRING())

MID() SUBSTRING() SUBSTRING()

UCASE() UPPER() UPPER(SUBSTRING())

StrConv() n/a n/a

TRIM() n/a n/a

CINT(data) -> CAST(data AS INT)

This function converts NUMERIC data that may be stored in string format to INTEGER format for comparison

and computation. Remember that SQL Server is much more strongly typed than VBA in Access, so you may

find yourself using CAST a lot more than you expected.

-- Access:
SELECT CINT(column)

-- SQL Server:
SELECT CAST(column AS INT)

INSTR(data, expression) -> CHARINDEX(expression, data)

This function returns an integer representing the character where the search expression is found within the

data parameter. Note that the order of these parameters is reversed!


-- Access:
SELECT INSTR("franky goes to hollywood","goes")

-- SQL Server:
SELECT CHARINDEX('goes','franky goes to hollywood')

ISDATE(data)

This function returns 1 if the supplied parameter is a valid date, and 0 if it is not. Aside from delimiters, the

syntax is identical.

-- Access:
SELECT ISDATE(#12/01/2001#)

-- SQL Server:
SELECT ISDATE('12/01/2001')

ISNULL(data)

This function works a bit differently in the two products. In Access, it returns 1 if the supplied parameter is

NULL, and 0 if it is not. In SQL Server, there are two parameters, and the function works more like a CASE

statement. The first parameter is the data you are checking; the second is what you want returned IF the

first parameter is NULL (many applications outside the database haven't been designed to deal with NULL

values very gracefully). The following example will return a 1 or 0 to Access, depending on whether 'column'

is NULL or not; the code in SQL Server will return the column's value if it is not NULL, and will return 1 if it is

NULL. The second parameter usually matches the datatype of the column you are checking.

-- Access:
SELECT ISNULL(column)

-- SQL Server:
SELECT ISNULL(column,1)

ISNUMERIC(data)

This function returns 1 if the supplied parameter is numeric, and 0 if it is not. The syntax is identical.
SELECT ISNUMERIC(column)

LEFT(data, n)

This function returns the leftmost n characters of data. The syntax is identical.

SELECT LEFT(column,5)

LEN(data)

This function returns the number of characters in data. The syntax is identical.

SELECT LEN(column)

LCASE(data) -> LOWER(data)

This function converts data to lower case.

-- Access:
SELECT LCASE(column)

-- SQL Server:
SELECT LOWER(column)

LTRIM(data)

This function removes white space from the left of data. The syntax is identical.

SELECT LTRIM(column)

REPLACE(data, expression1, expression2)

This function scans through data, replacing all instances of expression1 with expression2.

SELECT REPLACE(column, 'bob', 'frank')


RIGHT(data, n)

This function returns the rightmost n characters of data. The syntax is identical.

SELECT RIGHT(column,8)

RTRIM(data)

This function removes white space from the right of data. The syntax is identical.

SELECT RTRIM(column)

CSTR(data) -> STR(data)

This function converts data to string format.

-- Access:
SELECT CSTR(column)

-- SQL Server:
-- if column is NUMERIC:
SELECT STR(column)
-- if column is not NUMERIC:
SELECT CAST(column AS VARCHAR(n))

MID(data, start, length) -> SUBSTRING(data, start, length)

This function returns 'length' characters, starting at 'start'.

-- Access:
SELECT MID("franky goes to hollywood",1,6)

-- SQL Server:
SELECT SUBSTRING('franky goes to hollywood',1,6)

UCASE(data) -> UPPER(data)

This function converts data to upper case.


-- Access:
SELECT UCASE(column)

-- SQL Server:
SELECT UPPER(column)

StrConv

This function converts a string into 'proper' case (but does not deal with names like O'Hallaran or

vanDerNeuts). There is no direct equivalent for StrConv in SQL Server, but you can do it per word manually:

-- Access:
SELECT StrConv("aaron bertrand",3)

-- SQL Server:
SELECT LEFT(UPPER('aaron'),1)
+ LOWER(RIGHT('aaron',LEN('aaron')-1))
+ ' '
+ LEFT(UPPER('bertrand'),1)
+ LOWER(RIGHT('bertrand',LEN('bertrand')-1))

There is a thread stored at Google dealing with proper casing an entire block of text; you could likely

implement something like that in both Access and SQL Server.

TRIM(data)

This function combines both LTRIM() and LTRIM(); there is no equivalent in SQL Server. To mimic the

functionality, you would combine the two functions:

-- Access:
SELECT TRIM(column)
SELECT LTRIM(RTRIM(column))

-- SQL Server:
SELECT LTRIM(RTRIM(column))
String Sorting

Access and SQL Server have different priorities on string sorting. These differences revolve mostly around

special characters like underscores and apostrophes. These might not change the way your application

works, but you should be aware of the differences. Let's take this fictional example (SQL Server):

CREATE TABLE names


(
fname VARCHAR(10)
)
INSERT names VALUES('bob')
INSERT names VALUES('_bob')
INSERT names VALUES(' bob')
INSERT names VALUES('=bob')
INSERT names VALUES('andy')
INSERT names VALUES('_andy')
INSERT names VALUES(' andy')
INSERT names VALUES('=andy')
INSERT names VALUES('''andy')
INSERT names VALUES('''bob')
INSERT names VALUES('-andy')
INSERT names VALUES('-bob')
INSERT names VALUES('andy-bob')
INSERT names VALUES('bob-andy')

SELECT fname FROM names ORDER BY fname


DROP TABLE names

Now, insert identical data into a similar table in Access 2000, and compare the SELECT results:
SQL Server Access 2K
---------- ---------
andy andy
bob bob
'andy _andy
'bob _bob
-andy =andy
-bob =bob
=andy andy
=bob 'andy
_andy -andy
_bob andy-bob
andy bob
andy-bob 'bob
bob -bob
bob-andy bob-andy

Notice the inconsistencies - Access (like Windows) treats underscore (_) as the highest non-alphanumeric

character. Also, it ignores apostrophe (') and hyphen (-) in sorting. You can see the other slight differences

in sorting this otherwise identical list. At least they agree on which names are first and last... if only all of our

queries used TOP 1! Add on top of this that both database engines' concepts of sort order are sensitive to

changes in the underlying operating system's regional settings. SQL Server is also variable in its server-level

(and in SQL Server 2000, table- and column-level) collation options. So, depending on all of these variables,

your basic queries that sort on a text/char/varchar column will potentially start working differently upon

migration.

NULL Comparisons

SQL Server handles NULL values differently. Access assumes NULL = NULL, so two rows where a column is

<NULL> would match a JOIN clause comparing the two. By default, SQL Server treats NULLs correctly as

UNKNOWN, so that, depending on the settings within SQL Server, it cannot state that NULL = NULL. If you

are trying to determine whether a field contains a NULL value, the following query change should be made:
-- Access:
[...] WHERE column = NULL
[...] WHERE column <> NULL

-- SQL Server:
[...] WHERE column IS NULL
[...] WHERE column IS NOT NULL

If you set ANSI_NULLS OFF and are trying to compare two columns, they won't equate. A column that

contains a NULL will equate with an expression that yields NULL, as will two expressions that yield NULL. But

two columns that contain NULL will never be considered equal, regardless of ANSI_NULLS settings or the

ANSI standards. As a workaround, use the following comparison to determine that two fields are equal AND

both contain NULL (without the extra AND condition, these two would also evaluate as equal if they both

contained an empty string):

[...] WHERE ISNULL(col1,'') = ISNULL(col2,'') AND col1 IS NULL

Yes, it's not pretty. For more information on how SQL Server handles NULLs (and why you should avoid

them), see Article #2073.

OTHER SYNTAX CHANGES

There are possibly dozens of other slight syntax changes that may have to be made when moving from Access to

SQL Server. Here are a few of the more significant ones:

IIF(expression, resultIftrue, resultIfFalse)

IIF() is a handy inline switch comparison, which returns one result if the expression is true, and another

result if the expression is false. IIF() is a VBA function, and as such, is not available in SQL Server.

Thankfully, there is a more powerful function in SQL Server, called CASE. It operates much like SELECT CASE

in Visual Basic. Here is an example query:


-- Access:
SELECT alias = IIF(Column<>0, "Yes", "No")
FROM table

-- SQL Server:
SELECT alias = CASE WHEN Column<>0 THEN 'Yes' Else 'No' END
FROM table

SQL Server's CASE also supports multiple outcomes, for example:

SELECT alias = CASE


WHEN Column='a' THEN 'US'
WHEN Column='b' THEN 'Canada'
ELSE 'Foreign'
END
FROM table

DISTINCTROW

SQL Server supports DISTINCT but does not support DISTINCTROW.

OBJECTS

When creating tables and other objects, keep the following limitations in mind:

■ Access uses MAKE TABLE, while both platforms support CREATE TABLE;

■ Access object names are limited to 64 characters;

■ SQL Server 7.0+ object names are limited to 128 characters;

■ SQL Server 6.5 object names were limited to 30 characters and no spaces; and,

■ Stored queries in Access become Stored Procedures in SQL Server.

STORED QUERIES
Stored queries in Access are a way to store query information so that you don't have to type out ad hoc SQL all the

time (and update it throughout your interface everywhere you make a similar query). Being a non-GUI guy, the

easiest way I've found to create a stored query in Access is to go to Queries, open "Create query in Design View",

switch to SQL View, and type in a query, such as:

PARAMETERS ProductID INTEGER;


SELECT ProductName, Price
FROM Products
WHERE ProductID = [productID]

Be careful not to use any reserved words, like [name], as parameter names, or to give your parameters the SAME

name as the column -- this can easily change the meaning of the query.

Once you have the same schema within SQL Server, when moving to stored procedures, the basic difference you'll

need to know is syntax. The above stored query becomes:

CREATE PROCEDURE MyQuery


@ProductID INT
AS
BEGIN
SELECT ProductName, Price
FROM Products
WHERE ProductID = @productID
END

You can create this stored procedure using this code through QUery Analyzer, or you can go into the Enterprise

Manager GUI, open the database, open the Stored Procedures viewpane, right-click within that pane and choose New

> Stored Procedure. Paste the above code (or a query that might make a bit more sense given *your* schema), click

Check Syntax, and if it all works, click Apply/OK. Don't forget to set permissions!

Now in both cases, you can call this code from ASP as follows:
<%
productID = 5
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC MyQuery " & productID)
do while not rs.eof
' process recordset here
' ...
%>

See Article #2201 for a quasi-tutorial on writing stored procedures.

SECURITY

Access is limited to security in terms of username / password on the database. It also is subject to Windows security

on the file itself (as well as the folder it resides in). Typically, ASP applications must allow the anonymous Internet

guest account (IUSR_<machine_Name>) to have read / write permissions on file and folder. Username / password

access to the database cannot be controlled with any more granularity.

SQL Server has two authentication modes, and neither are much like Access security at all. You can use Windows

Authentication, which allows you direct access to domain Users and Groups from within the interface. You can also

use Mixed Mode, which allows SQL Server to maintain usernames and passwords (thereby negating the need for a

domain or other Windows user/group maintenance).

Once you have determined an authentication mode, users have three different levels of access into the database:

login (at the server level), user (at the database level), and object permissions within each database (for tables,

views, stored procedures, etc). Just to add a layer of complexity, SQL Server makes it easy to "clone" users by

defining server-wide roles, and adding users to that role. This is much like a Group in a Windows domain; in SQL

Server, you can use the built-in definitions (and customize them), or create your own. Alterations to a role's

permissions affect all users that are members of that role.

Microsoft has a thorough whitepaper you should skim through before jumping into SQL Server. If you're going to

deploy your own SQL Server box (as opposed to leasing a dedicated SQL Server, or a portion of one), by all means

read the SQL Server Security FAQ.


MORE INFORMATION

Article #2182 has a list of tools and tutorials that will aid in the migration process. Also be sure to read Microsoft's

migration whitepaper for some helpful info from the vendors themselves. Finally, if you're into books, APress has a

great title called From Access to SQL Server.


Schema: How do I get the tables out of a database?

6,240 requests - last updated Wednesday, September 19, 2001

Here are two ways to retrieve table names from a database. The first uses ADOX, and the second uses a system

stored procedure called sp_tables (which is therefore SQL Server only). In addition, check out sp_MSForEachTable

... this is an *undocumented* system procedure which does an enumeration of all tables in a database.

Here is the code that uses ADOX. This was tested with MDAC 2.5, and will work with either MS Access or SQL

Server.

<%
dbname = "databasename"

' Use this string if using Access:


' ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="
' ConnStr = ConnStr & "<path>\" & dbname & ".mdb"

' Use this string if using SQL Server:


ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set adoxConn = Server.CreateObject("ADOX.Catalog")


set adodbConn = Server.CreateObject("ADODB.Connection")
adodbConn.open ConnStr
adoxConn.activeConnection = adodbConn
for each table in adoxConn.tables
if table.type="TABLE" then
response.write table.name & "<br>"
end if
next
adodbConn.close: set adodbConn = nothing
set adoxConn = nothing
%>

And here is the code that uses a stored procedure (note that <uid> needs to have at least 'datareader' access to
<dbname>).

<%
dbname = "databasename"

ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set adodbConn = Server.CreateObject("ADODB.Connection")


adodbConn.Open ConnStr

set rs = adodbConn.execute("EXEC sp_tables")


do while not rs.eof
if rs("table_type") = "TABLE" then
Response.Write rs("table_name") & "<br>"
end if
rs.movenext
loop

rs.close: set rs = nothing


adodbConn.Close: set adodbConn = nothing
%>

Finally, for SQL Server, if you have access to Query Analyzer (and just want a table list, not necessarily for code),

you can simply type the following and hit F5:

EXEC sp_tables

-- or

SELECT name FROM sysobjects WHERE xtype='U' ORDER BY name


Schema: How do I get the column names (and their datatype) out of a table?

6,110 requests - last updated Friday, June 21, 2002

Here are three ways to retrieve column names from a table. The first uses ADOX, the second uses simple properties,

the third uses a system stored procedure called sp_help, and the fourth uses a system stored procedure called

sp_columns. The last two are therefore SQL Server only, and should not be relied upon in production code... future

changes in the product might break your code.

Here is the code for retrieving field names using ADOX, which will work with both MS Access and SQL Server:

<%
dbname = "databasename"
tablename = "tablename"

' Use this string if using Access:


' ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="
' ConnStr = ConnStr & "<path>\" & dbname & ".mdb"

' Use this string is using SQL Server:


ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

dim columnTypes(203)
columnTypes(2) = "SmallInt"
columnTypes(3) = "Integer"
columnTypes(6) = "Currency"
columnTypes(11) = "Boolean"
columnTypes(14) = "Decimal"
columnTypes(16) = "TinyInt"
columnTypes(129) = "Char"
columnTypes(131) = "Numeric"
columnTypes(135) = "DateTime"
columnTypes(200) = "VarChar"
columnTypes(203) = "Text"
set adoxConn = Server.CreateObject("ADOX.Catalog")
set adodbConn = Server.CreateObject("ADODB.Connection")
adodbConn.open ConnStr
adoxConn.activeConnection = adodbConn
set table = adoxConn.Tables(tablename)
for each col in table.columns
response.write col.name & " [" & columnTypes(col.type)
if col.type = 129 or col.type=200 then
' definedSize only works in SQL Server
Response.write " (" & col.definedSize & ")"
end if
Response.Write "]<br>"
next
set table = nothing
adodbConn.close: set adodbConn = nothing
set adoxConn = nothing
%>

Here is the code for using simple recordset properties:

<%
dbname = "databasename"
tablename = "tablename"

' Use this string if using Access:


' ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="
' ConnStr = ConnStr & "<path>\" & dbname & ".mdb"

' Use this string if using SQL Server:


ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

dim columnTypes(203)
columnTypes(2) = "SmallInt"
columnTypes(3) = "Integer"
columnTypes(6) = "Currency"
columnTypes(11) = "Boolean"
columnTypes(14) = "Decimal"
columnTypes(16) = "TinyInt"
columnTypes(129) = "Char"
columnTypes(131) = "Numeric"
columnTypes(135) = "DateTime"
columnTypes(200) = "VarChar"
columnTypes(203) = "Text"

set adodbConn = Server.CreateObject("ADODB.Connection")


adodbConn.open ConnStr
set rs = adodbConn.Execute("select * from " & tablename)
for each field in rs.fields
Response.Write field.name & " [" & columnTypes(field.type)
if field.type = 129 or field.type=200 then
' definedSize only works in SQL Server
Response.write " (" & field.definedSize & ")"
end if
Response.Write "]<br>"
next
rs.close: set rs = nothing
adodbConn.close: set adodbConn = nothing
%>

If you are using SQL Server, you can use sp_help, which returns 6 recordsets when there are no constraints, and 7

recordsets when there are constraints. You might have a query like this:
<%
dbname = "databasename"
tablename = "tablename"

ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set adodbConn = Server.CreateObject("ADODB.Connection")


adodbConn.Open ConnStr
set rs = conn.execute("EXEC sp_help '" & tablename & "'")
' ...
%>

And this would return the following columns, in successive recordsets:

Name, Owner, Type, Created_Datetime

Column_name, Type, Computed, Length, Prec, Scale, Nullable, TrimTrailingBlanks,


FixedLenNullInSource

Identity, Seed, Increment, Not For Replication

RowGuidCol

Data_located_on_filegroup

Index_name, Index_description, Index_keys

Constraint_type, Constraint_name, Status_enabled, Status_for_replication, Constraint_keys

Finally, here is the code for using sp_columns. Note that <uid> needs to have at least 'datareader' access to

<dbname>.
<%
dbname = "databasename"
tablename = "tablename"

ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set adodbConn = Server.CreateObject("ADODB.Connection")


adodbConn.Open ConnStr
sql = "EXEC sp_columns @table_name='" & tablename & "'"
set rs = adodbConn.execute(sql)
do while not rs.eof
response.write rs("column_name") & " [" & rs("type_name")
if rs("type_name")="varchar" or rs("type_name")="char" then
response.write " (" & rs("length") & ")"
end if
response.write "]<br>"
rs.movenext
loop
rs.close: set rs = nothing
adodbConn.Close: set adodbConn = nothing
%>

If you are only interested in SQL Server column names, you can execute one of the following queries:

SELECT name
FROM syscolumns
WHERE [id] = OBJECT_ID('tablename')

SELECT column_name
FROM information_schema.columns
WHERE table_name='tablename'

The latter is preferred, since it is a view that is less likely to be adversely affected by changes in the underlying

system tables.
Why do I get General error Unable to open registry key 'DriverId'?

5,505 requests - last updated Wednesday, October 2, 2002

The following error can happen when the Internet Guest Account (IUSR_<machine>) does not have sufficient

privileges on the MDB file (or the folder it resides in):

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC Microsoft Access Driver]General error Unable to open
registry key 'Temporary (volatile) Jet DSN for process 0x9ac Thread 0xa0c
DBC 0x15d1024 Jet'.

First thing you should do, is stop using ODBC drivers. If you use the JET OLE DB drivers instead (see Article #2126

for a sample connection string), at the very least you'll get a more meaningful error message.

Back to the permissions issue. This can happen right from the start, because proper permissions were never applied.

However, it can also happen when a compact and repair operation against the MDB file resets permissions. So, if

your database is getting corrupted often enough, you might find that compacting and repairing the database file also

leaves you with this error on your pages. (Of course, one way around this is to use a real database, if you can. See

Article #2195 for many of the other limitations of Access.)

If applying proper permissions doesn't solve the problem, or if they are already sufficient, then you may want to try

using an OLE-DB connection string instead of an ODBC and/or DSN connection (see Article #2126 for an example of

a DSN-less JET connection string). Also make sure to apply the most recent MDAC update

(http://www.microsoft.com/data/) and the most recent JET drivers (see Article #2342).

You may also want to check out these KB articles:

Q315456 FP: Error: Unable to Open Jet Temporary Key When You Attempt to Connect to Database

Q295297 PRB: Err Msg: 0x80004005: General Error Unable to Open Registry Key
Why do I get 80040E10 errors?

5,155 requests - last updated Wednesday, September 4, 2002

When creating SQL statements in ASP, you might encounter one of the following errors:

Microsoft JET Database Engine (0x80040E10)


No value given for one or more required parameters.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e10'


[Microsoft][ODBC Microsoft Access Driver] Too few parameters. Expected <n>.

or

Microsoft OLE DB Provider for SQL Server error '80040e10'


Procedure '<procedure>' expects parameter '<param>', which was not supplied.

This error is due to one of the following things:

1. A field name was spelled incorrectly.

2. One or more of the values was blank.

3. You tried to insert the wrong datatype (e.g. surrounded a numeric value with quotes).

To troubleshoot this, response.write your SQL statement... make sure there is data for all params you are passing,

and compare field names directly with those in the table. Also copy this SQL statement and execute it directly in the

database, if the error message and the response.write statement aren't already giving you enough information.
How do I know which version of MDAC I'm running?

5,087 requests - last updated Saturday, October 14, 2000

The simplest way is to use the version property of the ADODB.Connection object, such as:

<%
set conn = server.createobject("ADODB.Connection")
response.write conn.version
set conn = nothing
%>

Another way is to go to Start, Run... and type regedit, then look at the following key:

HKEY_LOCAL_MACHINE
\Software
\Microsoft
\DataAccess
\Version

You can also use the component checker, available halfway down this page, although it seems this tool hasn't been

updated for MDAC 2.6 yet.

Yet another way is to find msado15.dll (in %SYSTEM32%\DLLCache\), right-click it and hit properties, and look

on the version tab. This should produce the exact same result as the registry scan above.
How do I hide system tables in SQL Server's Enterprise Manager?

4,996 requests - last updated Tuesday, July 2, 2002

Many people don't like the clutter provided so graciously by the existence of all the system tables, objects and

stored procedures in SQL Server's user interface. To get rid of the system objects, just right-click the server in

question (in MMC), choose "Edit SQL Server Registration properties...", and uncheck "Show system databases and

system objects" ... hit OK and that clutter should disappear, making direct manipulation of your data much easier.

This will work in both SQL Server 7.0 and SQL Server 2000.
How do I connect to SQL Server on a port other than 1433?

4,556 requests - last updated Thursday, October 4, 2001

Sometimes an added measure of security can be achieved by using ports other than the defaults for server software.

SQL Server allows you to specify which port you want it to run on; the default is 1433. Provided you can access your

SQL Server through TCP/IP, the following connection string should help you connect to a different port (this example

uses port 1510 on the local machine):

<%
cst = "provider=SQLOLEDB;network=DBMSSOCN;uid=<uid>;"
cst = cst & "pwd=<pwd>;server=127.0.0.1,1510;database=pubs"
set conn = Server.CreateObject("Adodb.COnnection")
conn.open cst
...

Notice that the IP address and port number are separated by a comma, and that TCP/IP is 'forced' by adding

network=DBMSSOCN.
Why does ASP give me ActiveX errors when connecting to a database?

4,450 requests - last updated Friday, August 23, 2002

You may see the following errors after installing / re-installing / removing Access 97, Access 2000, Visual Studio, or

certain SQL Server components:

Microsoft VBScript runtime (0x800A01AD)


ActiveX component can't create object: <progid>

or

Microsoft VBScript runtime (0x8002801d)


Library not registered.

The problem is that operating systems prior to Windows 2000 had no way of preventing external applications from

removing or replacing critical system DLLs, like those installed with MDAC (Microsoft Data Access Components). To

fix this error, simply (re-)install the latest version of MDAC (http://www.microsoft.com/data/).
How do I prevent my ASP pages from waiting for backend activity?

4,334 requests - last updated Wednesday, August 14, 2002

If you are trying to run an executable or other backend process, see Adrian Forbes' article:

Executing long-running tasks from ASP

If you are trying to run long-running database tasks, then the following is designed for a very specific scenario. Your

ASP page should:

1. initiate a long-running command that may cause a browser to time out; and,

2. not need to show a user results from the command.

This means it should be a page which triggers an event in the database which is NOT a recordset or other results-

obtaining operation, and that the user isn't expecting direct feedback of any kind to let them know that the process

has finished.

<%
set conn = server.createobject("ADODB.Connection")
conn.open "<connection string>"
conn.execute "<long-running command>",,&H00000010
response.write "The command is still running, but I'm not waiting!"
...
%>

You can test this technique by creating a single-column table:

CREATE TABLE dt (dt DATETIME)

And using a command like:


<%
set conn = server.createobject("ADODB.Connection")
conn.open "<connection string>"
sql = "INSERT dt VALUES(CURRENT_TIMESTAMP) WAITFOR DELAY '00:00:05' " &_
" INSERT dt VALUES(CURRENT_TIMESTAMP) WAITFOR DELAY '00:00:05' " &_
" INSERT dt VALUES(CURRENT_TIMESTAMP)"
response.write now()
conn.execute sql,,&H00000010
'...
%>

Now wait at least 10 seconds, go into the dt table manually, and check out the differences in the dt column (and

compare to the now() value that you wrote to the browser).

One word of warning: errors get swallowed up by the provider when using asynchronous methods, so you'll want to

employ this method carefully.


How do I find a stored procedure containing <text>?

3,888 requests - last updated Friday, September 6, 2002

Both of these examples return the name and body of all stored procedures (excluding built-in procs with dt_ prefix)

that contain the specified string; in addition, they both highlight the searched string in the body of the procedure

when returned to the screen (the hW function is based on Article #2344).

Here is an example that uses the INFORMATION_SCHEMA views:

<%
set conn = Server.CreateObject("ADODB.Connection")
connstr = "provider=SQLOLEDB;server=<ip>;database=<db>;uid=<UID>;pwd=<PWD>"
conn.Open connstr

' String we're looking for:


str = "insert into blah"

sql = "SELECT SPECIFIC_NAME, ROUTINE_DEFINITION " & _


"FROM INFORMATION_SCHEMA.ROUTINES " & _
"WHERE ROUTINE_DEFINITION LIKE '%" & str & "%' " & _
" AND SUBSTRING(SPECIFIC_NAME,1,3)!='dt_'" & _
" ORDER BY SPECIFIC_NAME"

set rs = conn.execute(sql)
if not rs.eof then
do while not rs.eof
s = hW(str, server.htmlEncode(rs(1)))
s = replace(s, vbTab, "&nbsp;&nbsp;&nbsp;&nbsp;")
s = replace(s, vbCrLf, "<br>")
response.write "<b>" & rs(0) & "</b><p>" & s & "<hr>"
rs.movenext
loop
else
response.write "No procedures found."
end if

function hW(strR, tStr)


w = len(strR)
do while instr(lcase(tStr), lcase(strR)) > 0
cPos = instr(lcase(tStr), lcase(strR))
nStr = nStr & _
left(tStr, cPos - 1) & _
"<b>" & mid(tStr, cPos, w) & "</b>"
tStr = right(tStr, len(tStr) - cPos - w + 1)
loop
hW = nStr & tStr
end function

rs.close: set rs = nothing


conn.close: set conn = nothing
%>

If you are opposed to using INFORMATION_SCHEMA for whatever reason, then here is a simple example that uses

ADOX (however this approach is much less efficient):

<%
set catalog = Server.CreateObject("ADOX.Catalog")
set conn = Server.CreateObject("ADODB.Connection")

connstr = "provider=SQLOLEDB;server=<ip>;database=<db>;uid=<UID>;pwd=<PWD>"

catalog.ActiveConnection = connstr
conn.Open connstr

' String we're looking for:


str = "insert into blah"

' loop through all procedures:


For Each proc In catalog.Procedures

' last two characters are garbage


spname = left(proc.Name,len(proc.name)-2)

if left(spname, 3)<>"dt_" then


' use helptext to get command
SQL = "exec sp_helptext " & spname
set rs = conn.Execute(SQL)

sptext = "": disptxt = ""


do while not rs.eof
' each line is a row, build proc
sptext = sptext & rs(0) & "<br>"
disptxt = disptxt & server.HTMLEncode(rs(0)) & "<br>"
rs.movenext
loop
rs.close
set rs = Nothing

if instr(lcase(sptext),lcase(str))>0 then
' match!
disptxt = replace(disptxt, vbTab, "&nbsp;&nbsp;&nbsp;&nbsp;")
response.write "<b>" & spname & "</b><p>" & hW(str, disptxt) & "<hr>"
end if
end if
Next
conn.Close
set conn = Nothing
Set catalog = Nothing

function hW(strR, tStr)


w = len(strR)
do while instr(lcase(tStr), lcase(strR)) > 0
cPos = instr(lcase(tStr), lcase(strR))
nStr = nStr & _
left(tStr, cPos - 1) & _
"<b>" & mid(tStr, cPos, w) & "</b>"
tStr = right(tStr, len(tStr) - cPos - w + 1)
loop
hW = nStr & tStr
end function
%>
Can I fix this mm/dd/yyyy <-> dd/mm/yyyy confusion once and for all?

3,773 requests - last updated Wednesday, September 4, 2002

This seems to cause a lot of problems. In your environment, you may find that Access or SQL Server is set up for UK

dates, and Windows is set up for US dates. Or vice-versa. Or some other weird combination, like SQL Server is set

up for UK dates, the SQL machine's Windows is set up for US dates, but the current logged on user has a Danish

locale. Combine that with an ASP page on a Windows machine in the next rack, set up for a German charset, and

you can see there'll be some big trouble. Take a look at the dates formatted in this example:

<%
ReturnDateTime 1033, "English (US)"
ReturnDateTime 2057, "English (UK)"
ReturnDateTime 3081, "English (Australia)"
ReturnDateTime 1031, "German"

Sub ReturnDateTime(locale, description)


Session.LCID = locale
Response.Write "<b>" & description & " (LCID = " & locale & ")</b><br>"
Response.Write "Long date: " & FormatDateTime(Date, 1) & "<br>"
Response.Write "Short date: " & FormatDateTime(Date, 2) & "<p>"
End Sub
%>

This results in something like this:

English (US)
Long date: Monday, February 25, 2002
Short date: 2/25/2002

English (UK)
Long date: 25 February 2002
Short date: 25/02/2002

English (Australia)
Long date: Monday, 25 February 2002
Short date: 25/02/2002

German
Long date: Montag, 25. Februar 2002
Short date: 25.02.2002

So, to display dates to the user in the desired LCID is pretty trivial. Now, to see where the problems really come in

with the short date format, try this little script (assuming SQL Server):

<%
set conn = server.createobject("ADODB.Connection")
conn.open "<connectionString>"

' create a simple table


conn.execute("CREATE TABLE dt (d DATETIME)")

' simple sub to reset locale


Sub SetUSLocale()
Session.lcid = 1033
End Sub

Call SetUSLocale()

' sub for inserting date to DB


Sub InsertDateTime(locale, description)
session.lcid = locale
sql = "INSERT dt VALUES('" & FormatDateTime(date,2) &_
" " & time & "')"
on error resume next
conn.execute(sql)
if err.number <> 0 then
response.write description & " failed. " &_
err.description & "<p>"
err.clear
else
response.write description & " passed.<p>"
end if
End Sub
' let's insert current date / time in each locale
InsertDateTime 1033, "US English"
InsertDateTime 2057, "UK English"

' sub for displaying data from database


Sub ShowDatabaseDateTime(locale, description, dt)
response.write description & " unaltered: " & dt & "<br>"
session.lcid = locale
response.write description & " altered: " &_
formatdatetime(d,2) & " " & formatdatetime(d,3) & "<p>"
End Sub

set rs = conn.execute("SELECT d FROM dt")


do while not rs.eof
setUSLocale()
d = rs(0)
ShowDatabaseDateTime 1033, "US English", d
ShowDatabaseDateTime 2057, "UK English", d
rs.movenext
loop

rs.close
set rs = nothing

conn.execute("DROP TABLE dt")

conn.close
set conn = nothing
%>

Assuming your SQL Server and Windows machines are set up to accept dates in US format (mm/dd/yyyy), you

should see something like the following in the results:


US English passed.
UK English failed. The conversion of a char data type to a datetime data type resulted in an
out-of-range datetime value.

US English unaltered: 2/25/2002 9:53:33 PM


US English altered: 2/25/2002 9:53:33 PM

UK English unaltered: 2/25/2002 9:53:33 PM


UK English altered: 25/02/2002 21:53:33

The UK English insert statement failed because SQL Server doesn't know what month 25 is. In most VB / VBA

environments, such as Access, it will implicitly convert 25/02/2002 to 02/25/2002, figuring that the date was

entered incorrectly. SQL Server is not so forgiving, and for good reason -- there is ambiguity implied when a

database can make decisions for you. How does it know when to draw the line? Should it contemplate changing Nov.

12th to Dec. 11th or vice-versa? Of course not.

To help eliminate differences in international formatting of dates and times, it is recommended to always use

YYYYMMDD hh:mm:ss format. Unfortunately, ASP/VBScript doesn't have this format built in, so we have to

accommodate a bit. Here is a function I wrote to format a correct database-style date in standard ISO format,

regardless of the locale:

<%
Function dbDate(dt)
dbDate = year(dt) & left("00",2-len(month(dt))) &_
month(dt) & left("00",2-len(day(dt))) & day(dt) &_
" " & formatdatetime(dt,4)
End Function
%>

So, taking the above script, we can correct for differences in the LCID by passing the dates through dbDate() first

(note that the only changes in the script are marked in bold):
<%
Function dbDate(dt)
dbDate = year(dt) & left("00",2-len(month(dt))) &_
month(dt) & left("00",2-len(day(dt))) & day(dt) &_
" " & formatdatetime(dt,4)
End Function

set conn = server.createobject("ADODB.Connection")


conn.open "<connectionString>"

' create a simple table


conn.execute("CREATE TABLE dt (d DATETIME)")

' simple sub to reset locale


Sub SetUSLocale()
Session.lcid = 1033
End Sub

Call SetUSLocale()

' sub for inserting date to DB


Sub InsertDateTime(locale, description)
session.lcid = locale
sql = "INSERT dt VALUES('" & dbDate(now) & "')"
on error resume next
conn.execute(sql)
if err.number <> 0 then
response.write description & " failed. " &_
err.description & "<p>"
err.clear
else
response.write description & " passed.<p>"
end if
End Sub

' let's insert current date / time in each locale


InsertDateTime 1033, "US English"
InsertDateTime 2057, "UK English"
' sub for displaying data from database
Sub ShowDatabaseDateTime(locale, description, dt)
response.write description & " unaltered: " & dt & "<br>"
session.lcid = locale
response.write description & " altered: " &_
formatdatetime(d,2) & " " & formatdatetime(d,3) & "<p>"
End Sub

set rs = conn.execute("SELECT d FROM dt")


do while not rs.eof
setUSLocale()
d = rs(0)
ShowDatabaseDateTime 1033, "US English", d
ShowDatabaseDateTime 2057, "UK English", d
rs.movenext
loop

rs.close
set rs = nothing

conn.execute("DROP TABLE dt")

conn.close
set conn = nothing
%>

And the results this corrected script outputs are as follows:


US English passed.

UK English passed.

US English unaltered: 2/25/2002 10:03:00 PM


US English altered: 2/25/2002 10:03:00 PM

UK English unaltered: 2/25/2002 10:03:00 PM


UK English altered: 25/02/2002 22:03:00

US English unaltered: 2/25/2002 10:03:00 PM


US English altered: 2/25/2002 10:03:00 PM

UK English unaltered: 2/25/2002 10:03:00 PM


UK English altered: 25/02/2002 22:03:00

As alternatives to session.LCID, you can try maintaining an LCID in a specific session variable and setting it on a

page-by-page basis using @LCID or @CODEPAGE.

For more information on date / time formatting in ASP, please see the following KB articles:

Q218964

PRB: VBScript Date and Time Formats Change with Logged on User

Q264063

Date/Time Functions May not be Formatted Properly in Non-English (US) Locales

Q306044

INFO: Behavior of Date/Time Format Differs When Accessed from ASP

Finally, an alternative is to alter the default regional settings for the server, however this may impact other sites /

applications and should only be used as a last resort.

1. Log on to the server (either physically or through terminal services) and set the correct locale and date

format

2. Open regedit, navigate to the following key, and export it:


HKEY_CURRENT_USER\Control Panel\International

3. Open the exported file in Notepad, and replace "HKEY_CURRENT_USER" with "HKEY_USERS\.DEFAULT"

4. Save the file, double-click it, and restart IIS


Why do I get 800A0BB9 / 800A1391 errors?

3,678 requests - last updated Wednesday, August 28, 2002

Typically, the following error will happen if you try and set invalid cursor or lock properties on a recordset object.

ADODB.Recordset error '800A0BB9'


Arguments are of the wrong type, are out of
acceptable range, or are in conflict with one another.

This is often because you used the VB "friendly" names for the values (such as adLockReadOnly), instead of the

integer constants (which are the only values understood by the engine), without including ADOVBS.INC. So, a quick

solution can often be to make sure you've included ADOVBS.INC.

If you're using JScript, you might get the following error:

Microsoft JScript runtime error '800a1391'


'<variable>' is undefined

In this case, you should be including ADOJAVAS.INC or a more efficient workaround.

After that, you should investigate two things. One is whether you should even be using a recordset object; more

often than not, the ADODB.Recordset object is unnecessary (see Article #2191 for more info). The second is

whether it makes sense to include ADOVBS.INC / ADOJAVAS.INC, or to simply define the few constants you actually

plan on using (take a look at Article #2112 for reasons to avoid using this massive file).
How do I prevent NULLs in my database from mucking up my HTML?

3,619 requests - last updated Wednesday, July 17, 2002

Many people have come across the issue where they're using a recordset to populate a table, and someone goofed

up and stored <NULL> values in the database, so it wrecks the formatting of the table.

First, you should consider not allowing NULLs into your database in the first place; see Article #2073 for more info.

Now, let's say you can't get around using NULLs (due to pointy-haird boss syndrome, or whatever else)... you can

do one of two things:

1. test for null values at runtime, or

2. replace null values in the query itself

I prefer (2), but I will provide examples of both.

Here is an example of using VBScript to get rid of NULL values *after* they've come out of the database, but before

displaying them on the screen.

<%
' ...
do while not rs.eof
cCol = rs("column_which_may_contain_nulls")
if len(cCol)=0 then cCol = "&nbsp;"
response.write "<td>" & cCol & "</td>"
rs.movenext
loop
' ...
%>

Manas Tungare adds that there is an even quicker solution to this. By appending a blank string to the end of the

result, you implicitly force the value to become NOT null:


<%
' ...
do while not rs.eof
cCol = rs("column_which_may_contain_nulls") & ""
response.write "<td>" & cCol & "</td>"
rs.movenext
loop
' ...
%>

Here is an example of using SQL (both in Access and in SQL Server) to replace these NULL values for you, taking

care of the problem at the data level (because it *is* a data problem):

/* For Access: */

SELECT IIF(ISNULL(column),' ',column) FROM Table

/* For SQL Server: */

SELECT ISNULL(column,' ') FROM Table

-- or, more ANSI-compliant:

SELECT COALESCE(column,' ') FROM Table

(FWIW, I definitely like SQL Server's syntax better.)

In any case, I still strongly suggest you avoid NULLs in your database. But if you absolutely must have them, I hope

that these solutions help alleviate some of the problems they cause.
How do I enable connection pooling?

3,484 requests - last updated Sunday, June 10, 2001

Connection pooling is enabled by default for SQL Server and Oracle, in IIS 4.0 and above. You shouldn't need to

configure anything to take advantage of connection pooling for these databases. You can check the status of your

current settings by going to the "ODBC Data Sources" Control Panel applet (this is under Administrative Tools in

Windows 2000 and later). There is a tab called connection pooling, which is only relevant if you are using ODBC to

connect to your database(s) (yet another reason to use OLEDB and AVOID USING A DSN).

If you are using Access, and absolutely must use a DSN (e.g. pointy-haired boss syndrome), and you want to enable

connection pooling: double-click on the entry for 'Microsoft Access Driver (*.mdb)' and change 'Don't pool

connections to this driver' to 'Pool Connections to this driver.'

Please test the performance of your app with and without this setting. I'd be interested in knowing if this increased

or decreased connection and query times for ODBC connections to Access database.

Microsoft states:

"To make the best use of connection pooling, explicitly close database connections as soon as possible. By default, a

connection terminates after your script finishes execution. However, by explicitly closing a connection in your script

after it is no longer needed, you reduce demand on the database server and make the connection available to other

users."
Why do I get 'Syntax Error in INSERT INTO Statement' with Access?

3,297 requests - last updated Monday, July 8, 2002

Aside from an actual syntax error in your SQL statement, such as a misplaced quote, comma or bracket, the most

common cause for this error is by using a reserved word as an alias, column name or table name. For a list of

reserved words to check against your statement, see:

Article #2080

There is one word that keeps cropping up as the source for this error -- at least in Access -- though it is not

documented in the above article: password.

If you cannot change the name of the column that is using this reserved word, wrap it in [] brackets. E.g.:

SELECT password FROM users

becomes

SELECT [password] FROM users

Next, check your delimiters. You might get this if you try to delimit a numeric field with an apostrophe or quote, or a

date column with the wrong delimiter, or leave a string without a delimiter.

Finally, if you're still having a problem, change:

<%
conn.execute(sqlString)
%>

to:
<%
response.write(sqlString)
%>

That should point out the problem. If it doesn't, post your code, the resulting SQL, and your data structure to

microsoft.public.inetserver.asp.db and someone will try and help you.


How do I debug my SQL statements?

2,932 requests - last updated Monday, May 28, 2001

Many people who get errors in SQL statements post their ASP code and the error, and say "what's wrong with this

SQL statement?" It's hard for anyone to tell when you've got a SQL statement that hasn't been built yet, for

example:

<%
sql = "SELECT field FROM table WHERE ID=" & someVarname
set rs = conn.execute(sql)
%>

To debug this properly, when you get an error, you should add the following two lines:

<%
sql = "SELECT field FROM table WHERE ID=" & someVarname
response.write sql
response.end
set rs = conn.execute(sql)
%>

Now, you can copy the SQL statement from the browser and post it directly in Access or Query Analyzer, and see

why it's not working. Sometimes it will be obvious even before that point, e.g. someVarname is empty or the wrong

data type. Sometimes it will be missing (or erroneous) quotes (e.g. quotes around a numeric value, or # characters

around a SQL date). Sometimes it will be a reserved word problem (e.g. you named your field DATE).

Before posting SQL errors to the newsgroups, please assemble the following information:

■ DDL (in the form of CREATE TABLE statements) - this is so everyone can understand exactly what data types

your table consists of, and enables them to easily re-create your table in their own environment.

■ Sample data (in the form of INSERT statements) - this is so everyone can understand what data you put into

your table, and can populate their local copy for testing purposes.

■ The *actual* SQL statement (not the one made up with variables - if you can't get that working to begin

with, that's a string concatenation and/or variable issue).


Following these steps, you are likely to solve your problem much quicker than people who post their ASP code and

ask people to fix it. This will almost always result in a "we need more info" type of post.
How do I create a database from ASP?

2,904 requests - last updated Monday, July 22, 2002

SQL Server

Assuming you have sufficient permissions to the SQL Server box, you can simply say:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string with UID and NO database>"
conn.execute("CREATE DATABASE dummy")
' create tables, etc...
%>

The owner of the database will be the user specified in UID. To change the database owner, use sp_changedbowner,

as follows:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
conn.execute("USE dummy; EXEC sp_changedbowner 'username'")
' ...
%>

Now, if you have user-defined objects, put them in the model database. This is the database that is used to be a

template for new databases.

Access

Here is code that will create an empty Access database from ASP:
<%
newDB = "c:\inetpub\wwwroot\databases\new.mdb"
Set cat = Server.CreateObject("ADOX.Catalog")
cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & newDB
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & newDB
' create tables, etc...
%>

Another way to do it (if you're creating cookie-cutter databases) is to generate a template with table structure and

no data, and simply copy it:

<%
targetDB = "c:\inetpub\wwwroot\databases\new.mdb"
sourceDB = "c:\inetpub\wwwroot\databases\template.mdb"
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CopyFile sourceDB, targetDB, true
set fso = nothing
set conn = Server.CreateObject("ADODB.Connection")
conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & targetDB
' ...
%>

(Of course, you should check that the targetDB name is not already taken, or you could wipe out someone's data.)

Here is another set of samples that uses a bit more rigorous check for existing objects, and also creates a table in

the new database:

This version allows you to create a database in Access. Note that there is no error checking.
<%
db = Server.MapPath("/sampleDatabase.mdb")
tableName = "sampleTable"

'if you have JET installed:

dbCreate = "PROVIDER=microsoft.jet.oledb.4.0;data source=" & db

'otherwise:
'dbCreate = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" & db

set cat = Server.CreateObject("ADOX.Catalog")

if not fso.fileExists(db) then cat.create dbCreate

set conn = Server.CreateObject("ADODB.Connection")


conn.open dbCreate

cat.activeConnection = conn
tableExists = false

for i=0 to cat.tables.count-1


typ = cat.tables(i).type
nam = cat.tables(i).name
If typ = "TABLE" and lcase(nam) = lcase(tName) then
tableExists = true
End if
Next

if not tableExists then


tableCreate = "CREATE TABLE " & tName & "(" & _
"IDColumn AUTOINCREMENT," & _
"IntegerColumn INT," & _
"VarcharColumn VARCHAR(50)," & _
"MemoColumn MEMO DEFAULT '')"
conn.execute tableCreate
end if
conn.close
set conn = nothing
set cat = nothing
%>

And here is a version for SQL Server (note that you must connect to SQL Server with a user who has sufficient

privileges to create a new database). While I don't generally advocate the use of dynamic SQL, the following

procedure was concocted simply to demonstrate what you can do (and to avoid having to generate this SQL string

within an ASP page!):

CREATE PROCEDURE dbo.userProc_buildDatabase


@dbName SYSNAME,
@tableName SYSNAME
AS
BEGIN
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE CATALOG_NAME=@dbName
)
EXEC('CREATE DATABASE '+@dbName)

EXEC('IF NOT EXISTS (


SELECT 1
FROM '+@dbName+'.INFORMATION_SCHEMA.TABLES
WHERE TABLE_CATALOG='''+@dbName+'''
AND TABLE_NAME='''+@tableName+'''
)
CREATE TABLE '+@dbName + '.dbo.'+@tableName+'(
IDColumn INT IDENTITY(1,1),
IntegerColumn INT,
VarcharColumn VARCHAR(50),
TextColumn TEXT)')

END

Now you can call it from ASP as follows:


<%
db = "sampleDatabase"
table = "sampleTable"

set conn = Server.CreateObject("ADODB.Connection")


conn.open "provider=SQLOLEDB;server=???;database=Master;UID=???;PWD=???"

sql = "EXEC Master.dbo.userProc_buildDatabase " & _


" @dbName='" & db & "'" & _
",@tableName='" & table & "'"

conn.execute(sql)
conn.close
set conn = nothing
%>
Why does Access give me 'unspecified error' messages?

2,869 requests - last updated Wednesday, May 23, 2001

Aside from the improper use of MEMO fields (see Article #2188), this may be caused by the mode in which Access is

opened. Unless otherwise specified, IIS opens Access databases with adModeUnknown... which has proven to cause

random problems in certain configurations. You can overcome this by setting the mode to adModeReadWrite

*before* opening the connection, such as:

<%
cst = "Driver={Microsoft Access Driver (*.mdb)};DBQ="
cst = cst & server.mappath("/<pathtofile.mdb>")
set conn = Server.CreateObject("ADODB.Connection")
conn.mode = 3 ' adModeReadWrite
conn.open cst
%>

(If you know you have Jet 4.0 installed, you can use the following slightly more efficient method.)

<%
cst = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
cst = cst & server.mappath("/<pathtofile.mdb>")
set conn = Server.CreateObject("ADODB.Connection")
conn.mode = 3 ' adModeReadWrite
conn.open cst
%>
How do I access MIN, MAX, SUM, COUNT values from SQL statements?

2,801 requests - last updated Monday, July 8, 2002

Many people write a SQL statement like this:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("SELECT COUNT(column) FROM table")
response.write rs("column")
%>

And are confused when they get this error:

ADODB.Recordset (0x800A0CC1)
Item cannot be found in the collection corresponding to the requested name or ordinal.

This is because they weren't looking for "column" from the recordset, they were looking for an expression that

represents the COUNT of "column" in the table. There are at least three ways to obtain values of aggregate functions

from a recordset.

The first is to simply use ordinal numbers instead of the name of the column. This requires no modification to the

existing SQL statement, and has the added bonus of being slightly faster (though for most users I recognize this will

be negligible, and can make the ASP code harder to maintain if the SQL statement will change in the future).

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("SELECT COUNT(column) FROM table")
response.write rs(0)
%>

The second two solutions involving adjusting the SQL statement to use an alias for the expression:
<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"

set rs = conn.execute("SELECT columncount = COUNT(column) FROM table")


response.write rs("columncount")

set rs = conn.execute("SELECT COUNT(column) AS columncount FROM table")


response.write rs("columncount")
%>
How do I get rid of Named Pipes / DBNMPNTW errors?

2,769 requests - last updated Thursday, September 12, 2002

Chances are, you want to use TCP/IP, not Named Pipes. Make sure network library is configured to use TCP/IP by

default. If you have SQL Server installed, you can do this through the Client Network Utility.

Named pipes can sometimes be stubborn, even after you have changed your network library. This could be because

the remote serve is overriding your local settings. To alleviate this, add the following line to your existing connection

string:

<%
cst = cst & "Network=DBMSSOCN;"
%>
Can I compact / repair / synchronize an Access database from ASP code?

2,640 requests - last updated Thursday, September 5, 2002

Yes, you can compact and repair an Access database using ASP. Thanks to Craig Starnes for suggesting this article.

I created a simple table, with an Autonumber column and a text(50) column. I created a macro that would insert

rows with random characters into the text columns. I set the repeat on the macro to 30,000 and ran it. While I was

waiting for Access to struggle through the task and release my CPU so I could do other things on the box, I went

and brushed my teeth. When I came back, it wasn't finished yet. So I read Tolkien's trilogy. When it finally

completed the task, I observed the size of the MDB file, and it was around 1.3 MB. I then wrote a query to delete

roughly half the records, interspersed (e.g. I did not delete the first or last 15,000 rows - I wanted to see what

compacting would do to a more realistically adjusted Autonumber column). I closed the database, and lo and behold,

the file was still 1.3 MB (even though I had just cut the actual storage size in half).

So, I decided to try out Craig's code, which came to me something like this:

<%
oldDB = Server.MapPath("/accessTest.mdb")
bakDB = Server.MapPath("/accessTestBack.mdb")
newDB = Server.MapPath("/accessCompact.mdb")

Set FSO = Server.CreateObject("Scripting.FileSystemObject")

' back up database

FSO.CopyFile oldDB, bakDB, true

' compact database

Set Engine = Server.CreateObject("JRO.JetEngine")


prov = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
Engine.CompactDatabase prov & OldDB, prov & newDB
set Engine = nothing

' delete original database


FSO.DeleteFile oldDB

' move / rename our new, improved, compacted database

FSO.MoveFile newDB, oldDB


set FSO = nothing
%>

Sure enough, this code reduced the size of my MDB file by more than half (!) and did it rather quickly. I'm not sure

what would have happened to requests for the database that came in at exactly the same time, but if you're using

Access in a production environment, this is probably not a high priority anyhow.

Note that compacting the database also performs the 'repair' operation you might otherwise perform only through

Access' GUI.

For more ways to compact an Access database, see the following articles:

Online compacting of Access databases (ASPAlliance.com)

Compact and Repair (ASPerium.com)

Compact (compress) mdb database (pstruh.cz)

Note that in Access 97 / 2000, compacting a database would reset any tables with AUTONUMBER columns (if all

rows have been deleted, the next AUTONUMBER resets to 1; otherwise, it will be the next number available). With

Access XP, this is no longer the case (see Q287756).

If you are looking for information on how to synchronize multiple connections to a single database using ASP and

JRO, please see Q200300. The example there was written in VB, but you could easily port it to ASP.
How do I handle BIT / BOOLEAN fields in a query?

2,591 requests - last updated Tuesday, May 1, 2001

In Microsoft Access, you can say:

SELECT field FROM table WHERE boolField=TRUE


-- or
SELECT field FROM table WHERE boolField=FALSE

Unfortunately, this doesn't work in SQL Server. The following can be used in SQL Server:

SELECT field FROM table WHERE bitField=1


-- or
SELECT field FROM table WHERE bitField=0

However, this doesn't work in Access, since TRUE in Access is -1, not 1. Confused yet? :-)

For cross-platform support, use the following queries:

SELECT field FROM table WHERE bitField<>0


-- or
SELECT field FROM table WHERE bitField=0

-- to update (Access will convert to -1):


UPDATE table SET bitField=1
--or
UPDATE table SET bitField=0

Also, remember that if you're using checkboxes to update / insert / display data, you have to convert from "on" or ""

to 1 or 0, and when retrieving you must change 1 or 0 (or true or false) to "checked"... for example, page1.asp:
<form method=post action=page2.asp>
<input type=checkbox name=bool>
<input type=submit>
</form>

And page2.asp:

<%
bitValue = 0
bool = request.form("bool")
if bool = "on" then bitValue=1
sql = "UPDATE table SET bitField=" & bitValue
' ...
%>

And page3.asp:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("SELECT bitField FROM table")
ch=""
bitValue = rs(0)
if bitValue then ch=" CHECKED"
' or you could say if bitValue<>0 then ch=" CHECKED"
%>
<form method=post action=page2.asp>
<input type=checkbox name=bool <%=checked%>>
<input type=submit>
</form>

Hope that clears up some BIT / BOOLEAN misunderstandings.


How do I sort out an AND/OR query with an unknown number of parameters?

2,472 requests - last updated Sunday, April 1, 2001

Given this search page:

<form method=post action=find.asp>


<input type=text name=keywords>
<p>
<select name=kind>
<option value=' AND '>All words
<option value=' OR '>Any word(s)
</select>
<p>
<input type=submit>
</form>

The following ASP script will split up the search terms and do an AND or OR search appropriately:

<%
SQL = "SELECT field FROM table"

keywords = trim(replace(Request.Form("keywords"),"'","''"))

if len(keywords)>0 then
sqlExtra = " WHERE "
kind = Request.Form("kind")
keywordArray = split(keywords)
for i = 0 to ubound(keywordArray)
keyword = keywordArray(i)
' if you want to match entire words only!
sqlExtra = sqlExtra & " (field LIKE '% " & keyword & " %')" & kind
' if you want to find each string inside of other words
' sqlExtra = sqlExtra & " (field LIKE '%" & keyword & "%')" & kind
next
end if
response.write sql & left(sqlExtra,len(sqlExtra)-len(kind))
%>

Note that you may want to make your search case sensitive (in SQL Server you would do this through database

options, not in code). You also might want to have a list of noise words, such as 'a' and 'the' - particularly if you

don't use the WHOLE WORD search. For example, a search for 't' might return your entire table! Yes, that's the

user's fault, but it's still YOUR server doing all that extra work.
How do I solve 'ADO Could Not Find The Specified Provider'?

2,158 requests - last updated Saturday, August 17, 2002

Chances are, you are using an out-of-date version of Microsoft's Data Access Components. Get a new version from

MDAC Downloads at Microsoft's site.

See Q191271 and Article #2340 for more info.

(Reinstalling the most current MDAC components will automatically re-register the problem DLL, MSDASQL.dll.)
Schema: How do I get the stored procedures out of a database?

2,147 requests - last updated Sunday, April 15, 2001

The following SQL Statement will retrieve all the user-created stored procedures:

SELECT name FROM sysobjects


WHERE type='P' AND
ObjectProperty(ID,'IsMSShipped')=0
I get "Login failed for user '\'." in SQL Server, why?

2,109 requests - last updated Thursday, February 7, 2002

You have SQL Server set up to use Windows Authentication. Unless you are using Windows Authentication

exclusively throughout your web site, you should have SQL Server set up to authenticate using mixed mode - this

allows support for Windows Authentication AND passing credentials via clear text (e.g. a connection string from

ASP). To make sure SQL Server is set up to be in mixed mode:

1. Open Enterprise Manager;

2. Expand a server group;

3. Right-click a server, and click Properties;

4. Click the Security tab;

5. Under Authentication, click SQL Server and Windows.

Now, make sure you have a valid user set up to access your tables, views and procedures.

If you really want to use Windows Authentication, instead of a generic user, then see the following KB articles for

help on configuring a trusted connection:

Q306586 PRB: Troubleshooting Error 80004005 "Login Failed" in ASP

Q307002 PRB: ASP/ODBC/SQL Server Error 0x80040E4D "Login Failed for User '(Null)'"
How can I make my SQL queries case sensitive?

1,964 requests - last updated Thursday, August 22, 2002

If you installed SQL Server with the default collation options, you might find that the following queries return the

same results:

CREATE TABLE mytable


(
mycolumn VARCHAR(10)
)
INSERT mytable VALUES('Case')

SELECT mycolumn FROM mytable WHERE mycolumn='Case'


SELECT mycolumn FROM mytable WHERE mycolumn='caSE'
SELECT mycolumn FROM mytable WHERE mycolumn='case'

You can alter your query by forcing collation at the column level:

SELECT myColumn FROM myTable


WHERE myColumn COLLATE Latin1_General_CS_AS = 'caSE'

SELECT myColumn FROM myTable


WHERE myColumn COLLATE Latin1_General_CS_AS = 'case'

SELECT myColumn FROM myTable


WHERE myColumn COLLATE Latin1_General_CS_AS = 'Case'

If you want to do this in a more global way, instead of modifying each individual query, you can force the collation at

the database level, or at the column level, using the ALTER DATABASE and ALTER TABLE commands, respectively.

You can see the current collation level on the properties tab of the database server, through Enterprise Manager (if

you're going to change this setting, MAKE NOTE OF THIS VALUE):


And you can see the description from running the following query:

SELECT DATABASEPROPERTYEX('<database name>', 'Collation')

As changing this setting can impact applications and SQL queries, I would isolate this test first. In SQL Server 2000,

you can easily run an ALTER TABLE statement to change the sort order of a specific column, forcing it to be case

sensitive. First, execute the following query to determine what you need to change it back to:
EXEC sp_help 'mytable'

The second recordset should contain the following information, in a default scenario:

Column_Name Collation
----------- ----------------------------------------------
mycolumn SQL_Latin1_General_CP1_CI_AS

Whatever the 'Collation' column returns, you now know what you need to change it back to after you make the

following change, which will force case sensitivity:

ALTER TABLE mytable


ALTER COLUMN mycolumn VARCHAR(10)
COLLATE Latin1_General_CS_AS

SELECT mycolumn FROM mytable WHERE mycolumn='Case'


SELECT mycolumn FROM mytable WHERE mycolumn='caSE'
SELECT mycolumn FROM mytable WHERE mycolumn='case'

If this screws things up, you can change it back, simply by issuing a new ALTER TABLE statement (be sure to

replace my COLLATE identifier with the one you found previously):

ALTER TABLE mytable


ALTER COLUMN mycolumn VARCHAR(10)
COLLATE SQL_Latin1_General_CP1_CI_AS

If you are stuck with SQL Server 7.0, you can try this workaround, which might be a little more of a performance hit

(you should only get a result for the FIRST match):


SELECT mycolumn FROM mytable WHERE
CAST(mycolumn AS VARBINARY(10)) = CAST('Case' AS VARBINARY(10))

SELECT mycolumn FROM mytable WHERE


CAST(mycolumn AS VARBINARY(10)) = CAST('caSE' AS VARBINARY(10))

SELECT mycolumn FROM mytable WHERE


CAST(mycolumn AS VARBINARY(10)) = CAST('case' AS VARBINARY(10))
Schema: How do I show all the triggers in a database?

1,882 requests - last updated Sunday, June 30, 2002

The following code uses a pair of nested recordsets to grab the triggers from the sysobjects table and then display

them using sp_helptext (tested in SQL Server 2000):

<%
dbname = "databasename"

ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set Conn = Server.CreateObject("ADODB.Connection")


Conn.Open ConnStr

set rs = Conn.execute("SELECT name FROM sysobjects WHERE xtype='TR'")


do while not rs.eof
response.write("<hr>" & rs(0) & "<br>")
set rs2 = Conn.execute("EXEC sp_helptext '" & rs(0) & "'")
do while not rs2.eof
response.write(rs2(0) & "<br>")
rs2.movenext
loop
rs.movenext
loop

rs.close: set rs = nothing


Conn.Close: set Conn = nothing
%>

Note that you must loop through the sp_helptext resultset because, as with stored procedures, each line of code in a

trigger is returned as one row in the resultset.

Are there other ways to do this? Sure, here's some sample code, but it should be avoided due to its reliance on an

undocumented SP:
EXEC sp_msforeachtable @command1= "PRINT '?'
EXEC sp_helptrigger @tabname = '?'"

And this code, adapted from a post by Alejandro Mesa - which includes more information, but not the text for each

trigger in the current DB:

SELECT
[Table] = OBJECT_NAME(o.parent_obj),
[Trigger] = o.[name],
[Type] = CASE WHEN
(
SELECT
cmptlevel
FROM
master.dbo.sysdatabases
WHERE
[name] = DB_NAME()
) = 80 THEN
CASE WHEN
OBJECTPROPERTY(o.[id],
'ExecIsInsteadOfTrigger') = 1 THEN
'Instead Of'
ELSE
'After'
END
ELSE
'After'
END,
[Insert] = CASE WHEN
OBJECTPROPERTY(o.[id],
'ExecIsInsertTrigger') = 1 THEN
'Yes'
ELSE
'No'
END,
[Update] = CASE WHEN
OBJECTPROPERTY(o.[id],
'ExecIsUpdateTrigger') = 1 THEN
'Yes'
ELSE
'No'
END,
[Delete] = CASE WHEN
OBJECTPROPERTY(o.[id],
'ExecIsDeleteTrigger') = 1 THEN
'Yes'
ELSE
'No'
END
FROM
sysobjects o
WHERE
OBJECTPROPERTY(o.[id], 'IsTrigger') = 1
-- leave out this part of the where clause, to
-- include system triggers, e.g. those in MSDB
AND
OBJECTPROPERTY(o.[id], 'IsMSShipped') = 0
ORDER BY
1,2
Schema: How do I show all the primary keys?

1,838 requests - last updated Tuesday, April 30, 2002

The following code uses a recordset to grab the primary keys using a SELF JOIN on sysobjects:

<%
dbname = "databasename"

ConnStr = "provider=SQLOLEDB;network=DBMSSOCN;"
ConnStr = ConnStr & "uid=<uid>;pwd=<pwd>;server="
ConnStr = ConnStr & "<x.x.x.x>;database=" & dbname

set Conn = Server.CreateObject("ADODB.Connection")


Conn.Open ConnStr

sql = "SELECT so1.name AS keyName, so2.name AS tableName "


sql = sql & "FROM sysobjects so1 INNER JOIN sysobjects so2 "
sql = sql & "ON so2.id = so1.parent_obj WHERE (so1.xtype='PK')"
set rs = Conn.execute(sql)
do while not rs.eof
response.write(rs(0) & " is PK for " & rs(1) & "<br>")
rs.movenext
loop
rs.close: set rs = nothing
Conn.Close: set Conn = nothing
%>

To show the primary keys for a specific table, you can use the following methods:

EXEC sp_pkeys '<tablename>'


EXEC sp_helpconstraint '<tablename>'

sp_pkeys will return a row for each column that participates in the primary key for <tablename>. The columns you

are likely most interested in are COLUMN_NAME and PK_NAME.


sp_helpconstraint will list all constraints for <tablename>, including foreign keys that reference the table. In the

first recordset, there will only be a column called Object Name (kind of useless, since that's what you passed in). In

the second resultset, there will be the following columns: constraint_type, constraint_name, and constraint_keys.

Check these out in Query Analyzer. I will post ASP examples when I have a chance.
Why does my DELETE query not work?

1,737 requests - last updated Tuesday, June 26, 2001

Many people assume that DELETE queries are just like SELECT queries, in that you want to retrieve or affect a

certain group of columns hinging on a condition (e.g. WHERE clause). The syntax I generally see, and which causes

the most problems, is as follows:

DELETE * FROM table WHERE [...]

The problem here is that the DELETE command does not take columns as a parameter; you're deleting ROWS, not

COLUMNS. You can't delete just one field in records that match a given set of criteria; you delete the entire rows. So

you can change it to one of the following:

DELETE FROM table WHERE [...]


DELETE table WHERE [...]

(The latter usually scares people, but the two statements are functionally equivalent.)
How do I know which version of SQL Server I'm running?

1,724 requests - last updated Friday, October 18, 2002

For SQL Server 7.0 and 2000, the following will extract ONLY the version information. This has not been tested with

SQL Server 6.5 (sorry, can't find any of those anymore) but works from 7.0 through 2000 (8.0) SP2.

SELECT RIGHT(LEFT(@@VERSION,37),8)

And the following query will work on SQL Server 2000:

SELECT 'SQL Server '


+ CAST(SERVERPROPERTY('productversion') AS VARCHAR) + ' - '
+ CAST(SERVERPROPERTY('productlevel') AS VARCHAR) + ' ('
+ CAST(SERVERPROPERTY('edition') AS VARCHAR) + ')'

Or you can run this from ASP, assuming a valid and open connection to the SQL Server in an object named conn:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("SELECT RIGHT(LEFT(@@VERSION,37),8)")
response.write("SQL Version is: " & rs(0))
rs.close: set rs = nothing
%>

How do I interpret these results?

This query should result in one of the following strings:

7.00.517 = SQL Server 7.0 Beta 3

7.00.583 = SQL Server 7.0 RC1

7.00.623 = SQL Server 7.0 RTM

7.00.689 = SQL Server 7.0 SP1 Beta


7.00.699 = SQL Server 7.0 SP1

7.00.842 = SQL Server 7.0 SP2

7.00.843 = SQL Server 7.0 SP2 + Stored Procedure Patch

7.00.961 = SQL Server 7.0 SP3

7.00.1030 = SQL Server 7.0 SP3 + Q318268 Patch

7.00.1063 = SQL Server 7.0 SP4

7.00.1076 = SQL Server 7.0 SP4 + Q327068 Patch

7.00.1077 = SQL Server 7.0 SP4 + Q327068 Patch (updated 2002-10-02)

7.00.1078 = SQL Server 7.0 SP4 + Q327068 Patch (updated 2002-10-16)

8.00.194 = SQL Server 2000 RTM

8.00.384 = SQL Server 2000 SP1

8.00.532 = SQL Server 2000 SP2 Beta

8.00.534 = SQL Server 2000 SP2

8.00.578 = SQL Server 2000 SP2 + Q316333 Patch

8.00.608 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-04-17 for QFE Q356326 and QFE Q356938)

8.00.644 = SQL Server 2000 SP2 + Q324186 fix

8.00.650 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-07-10)

8.00.655 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-07-24)

8.00.665 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-08-14)

8.00.679 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-10-02)

8.00.686 = SQL Server 2000 SP2 + Q316333 Patch (Updated 2002-10-16)

If you are wary about using the string parsing provided (either for forward- or backward-compatibility), or are

worried your server might have another build of SQL Server (e.g. a beta of a service pack), you can roll your own

parsing simply by working with the results of the following:

SELECT @@VERSION

These results are a little superfluous, IMHO... but may contain any extra information you need. For example, they

also tell you which version of NT / Windows 2000 (including service pack level), the actual type of SQL Server

installed (for example, Enterprise Edition), and whether the system is Intel or Alpha. Finally, if you want even more

exhaustive information, you can use the following (undocumented) extended stored procedure:
EXEC master..xp_msver

See Q321185 for official Microsoft techniques.


How do I simulate an array inside a stored procedure?

1,657 requests - last updated Thursday, March 14, 2002

Often you will want to pass an array to a stored procedure and have the procedure loop through and process each

element in the array. Unfortunately, T-SQL does not have an array datatype.

Ken Henderson's new book, The Guru's Guide to SQL Server Stored Procedures, XML, and HTML, has a section on

dealing with arrays in T-SQL, with extended stored procedures (accompanied by source code) on the CD-Rom. His

method actually adds array support to T-SQL, rather than work around its absence.

While you're waiting for the book to arrive, what you can do instead, is pass in a list of comma-delimited strings and

parse it out, inserting it into a temp table of your choice. For example:

CREATE PROCEDURE dbo.FAQ_ListToTable


@cslist VARCHAR(8000),
@tablename SYSNAME AS
BEGIN
DECLARE @spot SMALLINT, @str VARCHAR(8000), @sql VARCHAR(8000)

WHILE @cslist <> ''


BEGIN
SET @spot = CHARINDEX(',', @cslist)
IF @spot>0
BEGIN
SET @str = CAST(LEFT(@cslist, @spot-1) AS INT)
SET @cslist = RIGHT(@cslist, LEN(@cslist)-@spot)
END
ELSE
BEGIN
SET @str = CAST(@cslist AS INT)
SET @cslist = ''
END
SET @sql = 'INSERT INTO '+@tablename+' VALUES('+CONVERT(VARCHAR(100),@str)+')'
EXEC(@sql)
END
END
Then you could use this procedure, for example, to do something like this:

CREATE TABLE #vals (id INT)


EXEC FAQ_ListToTable '1,2,3,5,7,8','#vals'
SELECT * FROM #vals

This eliminates the need, for example, to limit your lists to 4,000 or 2,667 characters when they're used multiple

times in a single dynamic SQL statement. Instead, you could just join against the #temp table.

With SQL Server 2000, you could implement a user-defined function (UDF) for this task. Please let us know if you

think an example of this code in the form of a UDF for SQL Server 2000 would be valuable.
Why doesn't SQL Server allow me to separate DATE and TIME?

1,631 requests - last updated Wednesday, October 31, 2001

Admittedly, this is one of the rare features that Access boasts over SQL Server. The ANSI-92 standard states that

compliant database should support the following DATE/TIME fields:

■ DATE + TIME

■ DATE

■ TIME

Unfortunately, SQL Server only supports the first type of column, with the DATETIME (sub-millisecond accuracy) and

SMALLDATETIME (minute accuracy) datatypes. If you only insert partial information (such as '10/31/2001' or '3:25

PM'), SQL Server will fill in the rest for you. Try the following script, to see what I mean:

SET NOCOUNT ON
CREATE TABLE #foo
(
dt DATETIME
)
INSERT #foo VALUES('10/31/2001')
INSERT #foo VALUES('3:25 PM')
SELECT dt FROM #foo
DROP TABLE #foo

Here are the results:

dt
-----------------------
2001-10-31 00:00:00.000
1900-01-01 15:25:00.000

Notice that SQL Server inserts midnight when time information is missing, and 1/1/1900 when date information is

missing.
So what do you do when you're only interested in one or the other? There are several camps on this one. One is to

store the date and/or time information as a CHAR or VARCHAR column. This makes comparisons and sorting very

difficult. Another camp suggests storing the extraneous information and ignoring it. Often "ignoring" means

"converting", so to get just the date or time from the above table, you would do this:

SELECT dateonly=CONVERT(CHAR(10),dt,101) FROM #foo


SELECT timeonly=CONVERT(CHAR(8),dt,8) FROM #foo

Results:

dateonly
----------
10/31/2001
01/01/1900

timeonly
--------
00:00:00
15:25:00

Unfortunately, this type of conversion will not take advantage of any index on the DATETIME column. A similar

approach is to store a standard value for the part you're not interested in, and handle that part of the data at the

application level.

Another way to store only time, and do so efficiently, is to use an integer field. You multiply the number of hours by

100, and add the minutes. For example:


SET NOCOUNT ON
CREATE TABLE #foo
(
tm SMALLINT
)
INSERT #foo VALUES
(
-- e.g. 1527 = 3:27 PM / 15:27

100 * DATEPART(HOUR, GETDATE())

+ DATEPART(MINUTE,GETDATE())
)
SELECT tm FROM #foo
DROP TABLE #foo

You multiply the hours by 100, then add the minutes, thus getting the time in military format. You could leave the

formatting up to the application, or retrieve a nicely formatted time by running this query against #foo (I think this

would be prettier in an application that's not so strongly typed):

SELECT
timeonly = CAST(
LEFT(tm, LEN(tm)-2) + ':'

+ RIGHT(tm, 2)

AS VARCHAR(5)
) FROM #foo

Results:

timeonly
--------
1:17
Similarly, to store only date as an integer, you multiply the year by 10000, add the month (multiplied by 100), and

then add the day:

SET NOCOUNT ON
CREATE TABLE #foo
(
dt INT
)
INSERT #foo VALUES
(
-- e.g. 20011031 = 2001/10/31

DATEPART(YEAR, GETDATE()) * 10000

+ DATEPART(MONTH, GETDATE()) * 100

+ DATEPART(DAY, GETDATE())
)
SELECT dt FROM #foo
DROP TABLE #foo

Getting this one into date format is about as pretty as the previous example:

SELECT
dateonly = LEFT(dt, 4) + '/'

+ LEFT(RIGHT(dt, 4), 2)

+ '/' + RIGHT(dt, 2) FROM #foo

Results:

dateonly
----------
2001/10/31
These latter solutions should only be used for enhancing storage space required for presentation values. If you need

to do computations and conversions on these columns, throw the last few solutions out the window.

In this age of infinite, nearly-free disk space, I think most SQL programmers would say "let the app people deal with

the formatting" while, conversely, the application developers would say "the data people should give me the format I

need."
Why do I get 'BOF or EOF' errors?

1,615 requests - last updated Thursday, July 18, 2002

When doing searches or other SQL queries, you may have encountered this error:

ADODB.Field error '80020009'

Either BOF or EOF is True, or the current record has been deleted; the
operation requested by the application requires a current record.

or

ADODB.Recordset error '800a0bcd'

Either BOF or EOF is True, or the current record has been deleted.
Requested operation requires a current record.

The most probable cause, of course, is that there is no record. For example, it would happen with the following code,

if none of the records cotnained 'frank' in the fname column:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open <connection string>
set rs = conn.execute("SELECT lname FROM table WHERE fname LIKE '%frank%'")
do while not rs.eof
response.write rs("lname") & "<br>"
rs.movenext
loop
%>

To prevent this error from "blowing up" your ASP page, you need to trap for the case where no records are there.

The easiest way to do this is by adding the following lines:


<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open <connection string>
set rs = conn.execute("SELECT lastname FROM table WHERE firstname LIKE '%frank%'")
if not rs.eof then
do while not rs.eof
response.write rs("lastname") & "<br>"
rs.movenext
loop
else
response.write "No matches."
end if
%>
How do I present one-to-many relationships in my ASP page?

1,613 requests - last updated Wednesday, January 23, 2002

Often you need to show results from multiple tables in one unified interface. Let's say you have the following

schema (geared to SQL Server):

CREATE TABLE artist


(
[id] INT IDENTITY,
[name] VARCHAR(32)
)

CREATE TABLE album


(
[artist_id] INT,
[name] VARCHAR(32)
)

INSERT INTO artist(name) VALUES('Michael Jackson')


INSERT INTO artist(name) VALUES('ABBA')
INSERT INTO artist(name) VALUES('Tragically Hip')

INSERT INTO album(artist_id,name) VALUES(1,'Bad')


INSERT INTO album(artist_id,name) VALUES(1,'Very Bad - Worst Hits')

INSERT INTO album(artist_id,name) VALUES(2,'Gold')

INSERT INTO album(artist_id,name) VALUES(3,'Self-Titled')


INSERT INTO album(artist_id,name) VALUES(3,'Up to Here')
INSERT INTO album(artist_id,name) VALUES(3,'Road Apples')
INSERT INTO album(artist_id,name) VALUES(3,'Fully Completely')
INSERT INTO album(artist_id,name) VALUES(3,'Day For Night')
INSERT INTO album(artist_id,name) VALUES(3,'Trouble at the Henhouse')
INSERT INTO album(artist_id,name) VALUES(3,'Live Between Us')
INSERT INTO album(artist_id,name) VALUES(3,'Phantom Power')
INSERT INTO album(artist_id,name) VALUES(3,'Music@Work')
The results you want in ASP are:

ABBA
Gold

Michael Jackson
Bad
Very Bad - Worst Hits

Tragically Hip
Self-Titled
Up to Here
Road Apples
Fully Completely
Day For Night
Trouble at the Henhouse
Live Between Us
Phantom Power
Music@Work

Now, what I see many people attempting in ASP is something like this (and I'll keep HTML simple for brevity):

<%
sql = "SELECT id,name FROM artist ORDER BY name"
set rs = conn.execute(sql)
do while not rs.eof
response.write rs("name") & "<p>"
sql2 = "SELECT name FROM album WHERE artist_id=" &_
rs("id") & " ORDER BY name"

set rs2 = conn.execute(sql2)


do while not rs2.eof
response.write rs2("name") & "<br>"
rs2.movenext
loop
response.write "<br>"
rs.movenext
loop
%>

There are much more efficient ways to do this than creating multiple recordsets and nesting them. This is what joins

are for. Imagine the above code if you then had a table with the song titles on the album? And then another table

listing the bands who have covered each song? And then a table further to that listing the albums of each of THOSE

bands?

Creating nested recordsets is very inefficient. Let's look at the join solution to the above:

<%
sql = "SELECT " &_
"artistName = artist.name, " &_
"albumName = album.name " &_
"FROM " &_
artist, " &_
album " &_
"WHERE " &_
"artist.id = album.artist_id " &_
"ORDER BY " &_
"artist.name, " &_
"album.name"

currentArtist = ""
set rs = conn.execute(sql)
do while not rs.eof
if rs("artistName") <> currentArtist then
response.write rs("artistName") & "<p>"
currentArtist = rs("artistName")
end if
response.write rs("albumName") & "<br>"
rs.movenext
loop
%>

One recordset, getting all your results, without causing extra strain on the database by executing multiple
statements consecutively. One large recordset is always more efficient than multiple recordsets obtaining the same

amount of data.

Now, one problem you might come acrosss is, what if an artist has no albums, but you still want to show the artist

name? Imagine adding the following artist, who couldn't possibly have any record deals in effect:

INSERT INTO artist(name) VALUES('Aaron Bertrand')

Now, the query and ASP code to still return this value is as follows (note how little it changes):

<%
sql = "SELECT " &_
"artistName = artist.name, " &_
"albumName = ISNULL(album.name,'-') " &_
"FROM " &_
"artist " &_
"LEFT JOIN " &_
"album " &_
"ON " &_
"artist.id = album.artist_id " &_
"ORDER BY " &_
"artist.name, " &_
"album.name"

currentArtist = ""
set rs = conn.execute(sql)
do while not rs.eof
if rs("artistName") <> currentArtist then
response.write rs("artistName") & "<p>"
currentArtist = rs("artistName")
end if
if rs("albumname") <> "-" then
response.write rs("albumName") & "<br>"
else
response.write "No albums for this artist.<br>"
end if
rs.movenext
loop
%>

We do a LEFT JOIN so that artists are included even if they have no matching records in the albums title.
Why do I get the error 'Command text was not set for the command object'?

1,545 requests - last updated Sunday, October 28, 2001

This error is usually caused by an empty SQL statement (and does not necessarily deal with the explicit

ADODB.Command object). Here is a sample piece of code that will cause this error:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"

' notice the flip flop of strSQL -> SQLstr

strSQL = "UPDATE table SET dt = CURRENT_TIMESTAMP"


conn.execute(SQLstr)

' ...
%>

Using Option Explicit would prevent errors like this from happening (well, actually, using Option Explicit would just

cause a different error). Using quick and sensible debugging practices will help determine the cause of many SQL-

related errors.
Why do I get weird results when using both AND and OR in a query?

1,325 requests - last updated Wednesday, May 30, 2001

Like grade school mathematics and the chicken and the egg, several fundamental concepts involving order of

operations also apply to SQL statements. Take the following, for example:

SELECT fields
FROM table
WHERE
a=1
AND b=2
OR a=2

I get confused reading that query, even after translating it to English:

GIVE ME the records


OUT OF this table
WHEN
a is 1
AND b is 2
OR a is 2

So, even with that straightforward English, which of these records would meet the criteria?

id a b
----------------------
1 1 2
2 2 2
3 2 1

Tricky, isn't it? The SQL engine, like me, has a hard time determining which clause(s) the AND applies to, and which

clause(s) the OR applies to. It really depends on what you mean by AND and what you mean by OR. You can

separate and group distinct clauses by using parentheses. So, as an example, if you mean:
GIVE ME the records
OUT OF this table
WHEN
a is 1 *AND* b is 2,

*OR*

WHEN a is 2

Then your SQL query would be:

SELECT fields
FROM table
WHERE
(a=1 AND b=2)
OR a=2

And if you mean:

GIVE ME records
OUT OF this table
WHEN
a is 1

*AND*

WHEN b is 2 *OR* a is 2

Then your SQL query would be:


SELECT fields
FROM table
WHERE
a=1
AND (b=2 OR a=2)
How do I connect to a non-default instance of SQL Server?

1,292 requests - last updated Monday, May 28, 2001

Here is a connection string that attaches to an instance of SQL Server that is NOT the default instance.

<%
cst = "Provider=SQLOLEDB;Server=127.0.0.1\instanceName;"
cst = cst & "database=pubs;network=DBMSSOCN;"
cst = cst & "uid=<uid>;pwd=<pwd>"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>

You could also connect to an alternate port, if that's where your instance is running:

<%
cst = "Provider=SQLOLEDB;Server=127.0.0.1,1510\instanceName;"
cst = cst & "database=pubs;network=DBMSSOCN;"
cst = cst & "uid=<uid>;pwd=<pwd>"
set conn = Server.CreateObject("ADODB.Connection")
conn.open cst
%>
Why do I get 80040E37 errors?

1,223 requests - last updated Sunday, August 25, 2002

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[Microsoft][ODBC Microsoft Access Driver] The Microsoft Jet database engine
cannot find the input table or query 'AddNew'. Make sure it exists and that
its name is spelled correctly.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[Microsoft][ODBC Excel Driver] The Microsoft Jet database engine could
not find the object '<file>.xls'. Make sure the object exists and that
you spell its name and the path name correctly.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[Microsoft][ODBC Visual FoxPro Driver]File '<file>.dbf' does not exist.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[OpenLink][ODBC][Progress Server]** Table item does not exist or
cannot be accessed. (962)

Make sure that the table exists, and that the file exists in the path specified. If you are using a linked table, make

sure that IUSR_machineName has permissions on the folder(s) hosting the 'remote' database files, and that the

links are still pointing to a valid location... use Access' 'Linked Table Manager' to correct this situation.

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid object name 'object'.

Make sure that the object exists *as you spelled it*, that the owner name is specified in your query (e.g.

dbo.tablename), that you are in the correct database (use databasename.ownername.tablename), and that the user
in your ASP page (e.g. the UID or User ID in your connection string) has access to the object. If you are sure the

object exists, see Article #2284.

If you are trying to use a variable for a table name in a query, use dynamic SQL instead; for example:

EXEC('SELECT columns FROM ['+ @table +']')

For more information on using dynamic SQL, see http://www.algonet.se/~sommar/dynamic_sql.html.

Microsoft OLE DB Provider for ODBC Drivers error '80040e37'


[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name '1'.

If you are trying to access a table, column or procedure name that begins with numerics, consider changing the

table name. You should never name objects with numbers... however in the meantime, you can probably surround

the object's name with [] square brackers to keep SQL Server quiet about it.
Should I index my database table(s), and if so, how?

1,193 requests - last updated Monday, January 7, 2002

An index is like a set of pointers to specific rows in a table. These pointers are ordered in terms of the column(s)

defined by the index, which makes SQL's scans much more efficient - they just look up the pointers to the rows with

the relevant data (based on a WHERE or other clause), and jump right to the row(s).

If you have multiple indexes on one column each, there will be n sets of pointers to the rows - each ordered by the

specific column. As I will discuss later, you should choose your index(es) wisely.

If you have one index on multiple columns, it creates one huge set of pointers -- ordering the rows by each column

you chose, successively.

So let's say you have a table with three integer columns (a, b and c). Let's insert some sample data, which are

stored on disk in heap format [remember - tables are not guaranteed to be sorted in any way], looking like this (a

normal SELECT a,b,c FROM table, given some fictitious data):

a b c
-----
1 2 3
2 2 2
1 2 2
3 1 2
2 1 2

Now, in the first example, we'll create a multi-column index on a, b and c. Now, this is how the pointers will look,

ORDER BY a, b, c:
a b c
-----
1 2 2
1 2 3
2 1 2
2 2 2
3 1 2

So now, imagine running a query SELECT a,b,c FROM table WHERE a=1. This query will be very efficient, because

the pointers have grouped these records all together.

Now, imagine running a similar query, but this time SELECT a,b,c FROM table WHERE b=1. This query will not be

very efficient, since SQL Server's only index option is this index which does NOT consider b to be a top priority (and

it just so happens that these records are NOT grouped together). It's the jumping around on the pointers that makes

SQL Server work harder to get all the rows that match the WHERE clause - in some cases it may be more efficient

for SQL Server to just do a table scan, rather than care about your index.

Let's create multiple indexes now, one on each column. The pointers for each, in order, will look something like this:

a b c
-----
1 2 2
1 2 3
2 1 2
2 2 2
3 1 2
a b c
-----
2 1 2
3 1 2
1 2 2
1 2 3
2 2 2

a b c
-----
1 2 2
2 1 2
2 2 2
3 1 2
1 2 3

Now, running the two queries mentioned above, each will be very efficient. In the first query, SQL Server will choose

the first index, and get all the rows where a is grouped together - minimizing read / scan time. In the second query,

SQL Server is smart enough to ignore the first index, and use the second index instead.

I will mention that with a compound index, let's say columns a, b and c, the optimizer will use the index for a query

on column a, or a and b, or a and b and c, or a and c. However, the index will not be able to optimize on queries

against column b, column c, or columns b and c.

Keep in mind that while an index can speed up SELECTs, it can also can slow down INSERTs and UPDATEs. In

addition, an index occupies disk space, which can be an issue not only for performance but also for backup /

replication purposes. Just something you should keep in mind before adding 19,000 indexes to a database - there is

definitely a happy medium between no indexes and too many.

Indexing in and of itself is a science, and is not easy to master without spending a lot of time analyzing different

indexes and their impacts on performance and disk space. And I didn't even start getting into fragmentation,

partitioning, clustered indexes, compound indexes...

What you use and what will work best depends on your schema, the nature of your queries, where your performance
counts, the load on your system, hardware, levels of transactions, acceptable query times, type of application, etc. I

strongly recommend running Index Profiler wizard, feeding it a SQL trace of the typical activity on your system. The

wizard should identify which types of indexes will work best for your scenario.
How do I determine if a number is odd or even?

1,172 requests - last updated Sunday, October 28, 2001

Sometimes you need to know this because it's important; other times, it's simply for flipping the colors of adjacent

rows in an HTML table. In any case, here are some samples of using the MODULUS operator on signed integers in T-

SQL, VBScript and JScript:

Transact-SQL

This mimics retrieving a resultset from a table, along with a description of whether each id is odd or even. Note that

ABS() is used so that the sign is ignored.

SELECT
id,
CASE
WHEN ABS(id) % 2 = 1 THEN 'odd'
ELSE 'even'
END
FROM table

VBScript

This uses a hardcoded value, so you can switch it around and test it before incorporating it into more significant

code.

<%
id = 5
stat = "even"
SELECT CASE abs(id) mod 2
CASE 1: stat = "odd"
END SELECT
Response.write id & " is " & stat
%>

JScript

This sample is just like the VBScript code above. The additional use of the Math object was required to convert the
integer to positive.

<script language='JScript' runat=server>


var id = 5;
var stat = "even";
switch(Math.abs(id) % 2)
{
case 1 : stat = "odd";
}
Response.Write(id.toString() + " is " + stat);
</script>
How do I enumerate through the DSNs on a machine?

1,067 requests - last updated Sunday, August 11, 2002

Well, there is a simply way, if SQL Server is installed on the same machine you're interested in:

EXEC master..xp_enumDSN

And then from ASP:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC master..xp_enumDSN")
if not rs.eof then
do while not rs.eof
response.write rs(0) & " (" & rs(1) & ")<br>"
rs.movenext
loop
else
response.write "No DSNs found."
end if
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

NOTE: You must have appropriate privileges to run extended stored procedures from the master database to pull

this off.

Another way is to use WMI's StdRegProv:


<%
Const HKLM = &H80000002
sPath = "Software\ODBC\ODBC.INI\ODBC Data Sources"

Set oReg = GetObject("winmgmts:root/default:StdRegProv")


o1 = oReg.EnumValues(HKLM, sPath, arNames, arTypes)

for i = 0 to ubound(arNames)
o2 = oReg.GetStringValue(HKLM, sPath, arNames(i), sValue)
response.write arNames(i) & " (" & sValue & ")<br>"
next
Set oReg = Nothing
%>

Again, appropriate privileges are required either for the anonymous or the authenticated user, depending on how

your security is configured.


What is this 'Multiple-step OLE DB operation generated errors' message?

1,064 requests - last updated Monday, September 23, 2002

You may have seen this error:

Microsoft OLE DB Provider for ODBC Drivers (0x80040E21)


Multiple-step OLE DB operation generated errors.
Check each OLE DB status value, if available. No work was done.

This is often a datatype problem. Make sure you are passing valid datatypes to whatever is going on in the

database. For example, check that you are not passing a NULL or empty string value to a column that doesn't accept

them (either manually defined or, say, a DATETIME column). Make sure that all VARCHAR lengths and numeric

bounds are adhered to.

Turn on on error resume next, and check the errors collection of the connection object. For example:

<%
set conn = server.createobject("ADODB.Connection")
conn.open "<connection string>"
sql = "<Statement that causes errors>"
on error resume next
conn.execute(sql)
if err.number <> 0 then
response.write(sql)
if conn.errors.count > 0 then
for each e in conn.errors
response.write e.description & "<br>"
next
end if
response.end
end if
%>

If this doesn't yield enough information, you might get a better error message out of the database itself, so take the

results of the response.write statement and issue it directly against the DB. If you are using an ADODB.Command
object, you might consider re-writing your code to execute the stored procedure directly, instead of using the

command object. (If you are relying on output or return parameters from the stored procedure, you will have to

alter the procedure to return those values as a resultset.)

If you are using "Persist Security info" in your connection string, try disabling it temporarily.

If you are using Driver={SQL Server} in your connection string, try Provider=SQLOLEDB instead (or vice-versa).

If you are connecting via an ODBC DSN, try using a DSN-less connection (see Article #2126.

If you are using the AddNew/Update methods of ADODB.Recordset, consider not doing so. Use an UPDATE or

INSERT statement instead. If you must, make sure you do an .UPDATE or .CANCEL before attempting to continue

further work on this or another record. See Q294160 for more info.

For a couple of other possibilities, see Q253157, Q269495, and Q228935.


How do I document / compare my SQL Server database(s)?

994 requests - last updated Wednesday, October 16, 2002

ASP developers thrown into a project involving SQL Server will undoubtedly need to get up to speed very quickly on

the database schema, structure, goals, etc. Unfortunately, not every company documents and maintains their

databases in a way that makes sense to newcomers.

Here is an article by Viktor Gorodnichenko:

Compare SQL Server Databases with sp_CompareDB

Aside from that, there are several third party vendors, offering products of varying complexity. You may choke at

the prices of some of these products, but remember that some of these tools are Enterprise-level and offer a *LOT*

more than just documentation.

AGS SQL Scribe

BMC Web DBA Free

BMC SQL Programmer Free

BMC SQL Programmer

CAST Release Builder

Cobb Systems Data Dictionary

Embarcadero DBArtisan

Enhanced ISQL/w

QALite

Quest SQL Impact


Red-Gate SQLCompare (with a companion article at MSSqlServer.com)

Total SQL Analyzer

WinSQL

Yes, you could document these things yourself using SQL Scripts, Database Diagrams, and what have you. But some

of these tools are downright cool, and for a few dollars, will save you from reinventing the wheel.
How do I get the number of rows in a table, or all tables?

937 requests - last updated Sunday, May 19, 2002

SQL Server

For any one table, you can get the number of rows with the following query:

SELECT COUNT(<columnname>) FROM <tablename>

There are some other interesting things you can quickly determine using SQL Server.

The following query gives a list of all user tables in the database, including the number of rows:

CREATE PROCEDURE FAQ_GetRowCounts


AS
BEGIN
SELECT
[TableName] = so.name,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.name
ORDER BY
2 DESC
END

(Note that this query will return a row for the 'dtproperties' table, which is not in the regular 'Tables' view in

Enterprise Manager's GUI.)


You can get this information into an ASP page by creating a stored procedure, and call it like so:

<%
' assuming valid/open connection object, conn
set rs = conn.execute("EXEC FAQ_GetRowCounts")
do while not rs.eof
response.write rs(0) & " has " & _
rs(1) & " rows.<br>"
rs.movenext
loop
rs.close
set rs = nothing
%>

The following will give you an idea of other parameters for a given table, including number of rows, reserved size,

and index size:

EXEC sp_spaceused '<tablename>'

To do this for all user tables, you can use the following (it will create multiple recordsets):

EXEC sp_MSForEachTable "EXEC sp_spaceused '?'"

(Note that sp_MSForEachTable is an undocumented SP and, as such, is not supported.)

And the following will provide similar information for the database as a whole (returns two resultsets):

EXEC sp_spaceused

You can easily call these procedures in much the same way.

Another way is to again use the undocumented stored procedure sp_MSForEachTable. PLEASE do not rely on this

method for production code, as its functionality could change in a future release of SQL Server - or even a service
pack.

First, create a stored procedure:

CREATE PROCEDURE FAQ_GetRowCounts


AS
BEGIN
SET NOCOUNT ON
CREATE TABLE #tmp (
[table] VARCHAR(64),
[rows] INT
)

INSERT INTO #tmp


EXEC sp_MSForEachTable
"SELECT
[table]=SUBSTRING('?',8,LEN('?')-8),
[rows]=COUNT(*) FROM ?"

DECLARE @rowCount INT


SELECT @rowCount = SUM([rows]) FROM #tmp

IF @rowCount > 0
SELECT rowCount = @rowCount

SELECT
[table],
[rows],
-- get the percentage of space used by table
[weight] = CAST(
[rows]*100.0 / @rowCount
AS SMALLMONEY
)
FROM #tmp
ORDER BY 2 DESC

DROP TABLE #tmp


END
Now call it from ASP as follows:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open <connection string>
set rs = conn.execute("EXEC FAQ_GetRowCounts")
if not rs.eof then
response.write "<TABLE><TR><TD COLSPAN=3>"
response.write rs(0) & " Total Rows</TD></TR>"

response.write "<TR><TH>Table</TH>"
response.write "<TH>RowCount</TH>"
response.write "<TH>Share of Whole</TH></TR>"

set rs = rs.nextRecordSet()
do while not rs.eof
response.write "<TR><TD>" & rs(0) & "</TD>"
response.write "<TD align=right>" & rs(1)
response.write "</TD><TD align=right>" & rs(2)
response.write "%</TD></TR>"
rs.movenext
loop
response.write "</TABLE>"
else
response.write "No tables?"
end if
%>

Access

For Access, you can - of course - use the very first method described in the top of this article. To iterate through

each table, though, you have to do a little more monkeying around in ASP, because there are no such stored queries

and there are no shortcuts, at least that I know of. You can use this method for SQL Server as well, if you're wary of

using undocumented stored procedures -- just change the connection string information to be SQL Server-based.

Note that this code *looks* simpler than the above, but there are two reasons - one is that the performance is
abysmal, and two is that the formatting isn't as pretty.

<%
dbname = "databasename"

ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="


ConnStr = ConnStr & "<path>\" & dbname & ".mdb"

set adoxConn = Server.CreateObject("ADOX.Catalog")


set adodbConn = Server.CreateObject("ADODB.Connection")
adodbConn.open ConnStr
adoxConn.activeConnection = adodbConn
for each table in adoxConn.tables
if table.type="TABLE" then
response.write table.name & " - "
set rs = conn.execute("SELECT COUNT(*) FROM " & table.name)
response.write rs(0) & "<p>"
end if
next
adodbConn.close: set adodbConn = nothing
set adoxConn = nothing
%>
Where can I get this 'Books Online' that people keep telling me about?

907 requests - last updated Monday, January 7, 2002

Books Online is fundamental documentation for SQL Server. If you are developing from a machine that does not

have SQL Server installed, you can (and SHOULD) download this resource from

http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp ... the English version is 32MB, but trust me -- it

is more than worth it.


Why do I get SQLSetConnectAttr Failed errors?

890 requests - last updated Saturday, September 21, 2002

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


[Microsoft][ODBC Driver Manager] Driver's SQLSetConnectAttr failed

In most cases, this error message is purely informational, and does not indicate that anything terrible has happened

while connecting to the database. To avoid the error, you can use on error resume next, and then check each

error in the conn.errors collection -- if any number is 0, it can be ignored.

See Q197459 and Q241267 for more information.


Why do I get 80040e31 / 'Timeout Expired' errors?

886 requests - last updated Thursday, July 18, 2002

I have heard many people saying that their web site is chugging along, and suddenly they get this error:

Microsoft OLE DB Provider for ODBC Drivers (0x80004005)


[Microsoft][ODBC SQL Server Driver]Timeout expired

or

Microsoft OLE DB Provider for SQL Server error '80040e31'


Timeout expired

This can often be fixed by "kicking" the server, e.g. hitting refresh on the affected ASP page eliminates the problem.

But how about a longer-term fix?

If you have MDAC 2.6 installed, upgrade to the latest version of MDAC from http://www.microsoft.com/data/. As an

immediate workaround, force TCP/IP lookup in your connection string and make sure you refer to the SQL Server by

IP address, not by name. For more info see Article #2082 and Article #2126.

The timeout can be caused by a name -> ip lookup. This is a problem with the version of DBNetLib.dll that shipped

with MDAC 2.6 -- see Q300420 for more information.

While a quick bandage would be to increase timeout values (see Article #2066), a better approach would be to make

your queries more efficient. Several approaches to this include:

■ encapsulate all data access using stored procedures, instead of ad hoc queries (see Article #2201)

■ using SET NOCOUNT ON in all stored procedures

■ avoiding ADODB.Recordset and ADODB.Command objects when they're not necessary (see Article #2191)

■ using the adExecuteNoRecords flag (&H00000080) on conn.execute calls that don't return resultsets

■ choosing appropriate indexes, primary keys, data types, and query structures

■ using GetRows() or GetString() instead of processing the recordset object


■ avoid storing recordsets or connections in session or application variables (see Article #2053)

■ try not to do too much database work with a single script... I often see errors like this happening on

/file.asp, line 1294 -- which is *way* too many lines of ASP code to (a) manage and (b) expect to run

efficiently

■ making sure your network connection between web server and database server is not the bottleneck
How do I solve 'Could not find installable ISAM' errors?

770 requests - last updated Friday, February 22, 2002

Using a DSN or DSN-less connection, with a JET/OLEDB or Access/ODBC driver, you may get this message:

Microsoft OLE DB Provider for ODBC Drivers error '80004005'


'or
'Microsoft JET Database Engine error '80004005'
Could not find installable ISAM.

The easiest fix for this, if you have access to the server, is to reinstall MDAC. If you are running on a web host, you

will need to ask them to fix their data components.

There are also registry-specific KB articles detailing what the MDAC reinstallation accomplishes.

Access XP: Q283881

Access 2K: Q209805

Access 97: Q90111


Why do I get 'Argument data type text is invalid for argument [...]'?

730 requests - last updated Saturday, September 14, 2002

SQL Server TEXT columns are kind of special. Because of their size, and the fact that they're stored off-row, string

manipulations must be considered wisely. Also, some of the functions that work for VARCHAR are not legal with

TEXT. Here is a partial list (these samples assume a TEXT column called 'data'):

-- getting the number of characters

SELECT LEN(data)
-- becomes
SELECT DATALENGTH(data)

-- getting the first 5 characters

SELECT LEFT(data,5)
-- becomes
SELECT SUBSTRING(data,1,5)

-- getting the last 5 characters

SELECT RIGHT(data,5)
-- becomes
SELECT SUBSTRING(data,DATALENGTH(data)-4,5)

-- changing case

SELECT LOWER(data)
SELECT UPPER(data)
-- becomes
SELECT LOWER(SUBSTRING(data,1,DATALENGTH(data))
SELECT UPPER(SUBSTRING(data,1,DATALENGTH(data))

-- replacing part of the string

SELECT REPLACE(data,'bob','frank')
-- becomes
SELECT STUFF(data,CHARINDEX('bob',data),LEN('bob'),'frank')

Take care with the STUFF function. You should always check if the column contains the target string, otherwise your

column could become <NULL>.


Can I start IDENTITY values at a new seed?

729 requests - last updated Monday, January 7, 2002

This can be useful if you want to create an artificial gap between your current seed and your desired next identity

value.

SET IDENTITY_INSERT [myTable] OFF


INSERT INTO [myTable] (id,other)
VALUES(@NewSeed,'something')
SET IDENTITY_INSERT ON [myTable] ON

In this case, the id column will continue incrementing from the vaue of @NewSeed.

If you want to remove all entries in a table and start over at 1 (or a non-default seed), you can do one of two

things:

DROP TABLE [myTable]


CREATE TABLE [myTable]
(
-- Column definition
)

-- or

TRUNCATE TABLE [myTable]

Also, check out DBCC CHECKIDENT in Books Online.


How do I time my T-SQL code?

722 requests - last updated Wednesday, January 30, 2002

This is for SQL Server specifically. With Access, you'll likely have to rely on the methods described in Article #2092

...

The first thing you can do is simply compare the difference between the timestamp BEFORE your query, and the

timestamp AFTER. For example:

DECLARE @a DATETIME, @b DATETIME


SET @a = CURRENT_TIMESTAMP

DECLARE @i INT
SET @i = 0
WHILE @i < 10000
BEGIN
SET @i = @i + 1
END
SET @b = CURRENT_TIMESTAMP
SELECT DATEDIFF(MS, @a, @b)

You can achieve similar results by running SQL Profiler, setting appropriate filters, and watching the Duration column

as your query runs.

Finally, you can alter the above code slightly so that you see all of the durations on the messages tab of Query

Analyzer:

SET STATISTICS TIME ON


-- query here
SET STATISTICS TIME OFF

Then if you look in Query Analyzer's Messages tab, you will see the number of milliseconds taken by each step in

your query.
Obviously, this is much more useful for queries with a reasonable amount of unique queries, and doesn't do much

good for code with loops. This particular STATISTICS option prints durations for every single operation (each

iteration of a loop is recorded), so there will be 10,000 messages, likely all stating 0 ms. This could really impact the

time it takes to execute a query.

What you can do after this, to compare two queries (and perhaps get to the bottom of why one takes longer than

the other), is to turn on Show Execution Plan (CTRL+K) and view that tab after your queries are finished. You'll be

able to spot table scans and other operations with high I/O or CPU costs.
Why do I get 800A0CC1 errors?

709 requests - last updated Sunday, August 18, 2002

When working with a resultset in ASP and you do this:

<%
...
Response.Write(rs("columnName"))
%>

... you may get the following error:

ADODB.Recordset (0x800A0CC1)
Item cannot be found in the collection corresponding to the requested name or ordinal.

... even though columnName is clearly being returned in the resultset when you run the procedure from Query

Analyzer.

Sometimes this is because you've misspelled the column name, referred to an aggregate without using an alias (see

Article #2159), referenced a column name that appears more than once in the SELECT list (e.g. a JOIN between two

tables that have a common column), or even referenced the wrong resultset in the case of multiple resultsets. If you

are having one of these problems, you can correct them quite easily by referencing the column correctly, or the

correct recordset. If you have a JOIN that has two columns with the same name, you probably have a design issue

because (a) if the columns contain the same value, you don't need both in the resultset; and (b) if the columns don't

contain the same value, then they have different meanings, and therefore should have distinct names.

But most often it is because you're using a stored procedure, and ADO is incorrectly interpreting messages in QA's

messages tab, for example:

1 row(s) affected

... as a resultset, however there are no columns in the data pane, so it is useless except for debugging within Query

Analyzer.
You can correct this behavior by adding the following to the top of your procedure:

SET NOCOUNT ON

This prevents the messages from factoring into your ASP code.

If your database code is not changeable, you might experiment with adding set rs = rs.nextRecordset() lines until

you hit the first 'real' resultset.

The use of SET NOCOUNT ON is demonstrated in Article #2201 ...


Why do I get 80040e09 errors?

707 requests - last updated Wednesday, July 17, 2002

If you are developing with MS Access, you've almost certainly seen this error message before:

Microsoft OLE DB Provider for ODBC Drivers error '80040e09'


or
Microsoft JET Database Engine error '80040e09'

There are many different error messages that can go along with this.

Record(s) cannot be read; no read permission on '<table name>'.

or

Cannot update. Database or object is read-only.

Make sure IUSR has access to the database (for more information, see Article #2062).

With SQL Server, the error message might be a little bit different:

[Microsoft][ODBC SQL Server Driver][SQL Server]


EXECUTE permission denied on object '<proc name>', database '<database name>', owner '<owner
name>'.

...but again, make sure the user you're connecting as has appropriate access to the table, stored procedure or any

other object you are attempting to access.

Another possible problem could be trying to issue an Update or Resync command on an ADODB.Recordset object

based on a SQL Server view:


[Microsoft][ODBC SQL Server Driver][SQL Server]
SELECT permission denied on object '<table name>', database '<database name>', owner '<owner
name>'.

See Q253673 for information on resolving this issue.

Some databases will toss this error if you put quoted identifiers around table or column names, e.g.

SELECT column FROM table WHERE 'column_name'=1

Quotes are for string literals, not object names.

With MySQL, this error can come up if you have a malformed SQL statement.

Microsoft OLE DB Provider for ODBC Drivers error '80040e09'


[TCX][MyODBC]You have an error in your SQL syntax near '<something>' at line 1

Can't offer much here, except to debug your SQL statement (see Article #2145) and make sure its format is correct.
How do I temporarily disable a trigger?

687 requests - last updated Sunday, October 28, 2001

Some people have found a need to disable a trigger for a short period of time while they did work on the system

that was not typical for their application. Rather than delete the trigger and re-create it later, here is some code that

you can run in one statement (and inside a transaction, if required):

ALTER TABLE tablename DISABLE TRIGGER ALL


-- do work here
-- and after
ALTER TABLE tablename ENABLE TRIGGER ALL
Why do I get 80040E57 / 80040E07 errors?

637 requests - last updated Saturday, September 14, 2002

When dealing with CHAR/VARCHAR columns, and not validating user input, you may have come across this error:

Microsoft OLE DB Provider for SQL Server error '80040e57'


String or binary data would be truncated.

or

Microsoft JET Database Engine (0x80040E57)


The field is too small to accept the amount of data you attempted to add.
Try inserting or pasting less data.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e57'


[Microsoft][ODBC Microsoft Access Driver]Invalid string or buffer length.

Typically, this is caused by trying to insert too many characters into a defined column. For example:

CREATE TABLE #foo (bar CHAR(5))


INSERT #foo(bar) VALUES('yoohoo')

A couple of easy fixes include:

■ using client-side methods (e.g. MAXLENGTH or JavaScript) to prevent such data from being submitted;

■ adjusting the column definition(s) to accommodate for likely data lengths;

■ adjusting the incoming data to fit the column definition, e.g.:


<%
bar = replace(left(request.form("bar"),5),"'","''")
sql = "INSERT #foo(bar) VALUES('" & bar & "')"
' ...
%>

There may be another reason for this symptom, if you are seeing this error from ASP but do not see it in native

tools; for example, Query Analyzer. You can alleviate this by making SET database options consistent -- see

Q255765 for more information.

Microsoft OLE DB Provider for ODBC Drivers error '80040e57'


[Microsoft][ODBC Microsoft Access Driver]Numeric value out of range (null)

Check that you are inserting valid numbers into numeric columns. These should not contain any non-numeric

characters, and should not be enclosed in quotes.

Microsoft JET Database Engine error '80040e57'


Overflow

Make sure you are inserting numeric values into a numeric column, and that it doesn't exceed the capacity of the

column. For example, 3,000,000,000 won't fit in a standard Access numeric (or SQL Server INT) column.
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]
The conversion of a CHAR data type to a DATETIME data type resulted in an
out-of-range DATETIME value.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'


[Microsoft][ODBC SQL Server Driver][SQL Server]
The conversion of CHAR to DATETIME resulted in a DATETIME value out of range.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'


[Microsoft][ODBC Microsoft Access Driver] Data type mismatch in criteria expression.

or

Microsoft JET Database Engine error '80040e07'


Data type mismatch in criteria expression.

This error usually happens when you do one of the following things:

■ attempt to insert a date in Access with ' delimiters;

■ attempt to insert a date in SQL Server with # delimiters;

■ attempt to insert a date in Access or SQL Server with no delimiters; or,

■ attempt to insert a malformed date.

If you are using a TEXT column for the first time, you might find that it does not behave the same as

CHAR/VARCHAR. You might come across an error like this:

Microsoft OLE DB Provider for SQL Server (0x80040E07)


Argument data type text is invalid for argument 1 of len function.
See Article #2061 for a list of workarounds to using standard string functions against TEXT columns.
Why does AbsolutePosition return as -1?

612 requests - last updated Wednesday, January 30, 2002

This could be because the provider you selected doesn't support AbsolutePosition. But it is more likely that you are

not opening the cursor with the correct properties. Make sure you are using a dynaset or snapshot cursor; this

means that adOpenForwardOnly should be used in place of adOpenStatic, and the CursorLocation should be set to

adUseClient.
Where else can I learn about SQL Server?

611 requests - last updated Sunday, November 10, 2002

There are several resources devoted to SQL Server. Here are Microsoft's:

If you don't already have it installed, download Books Online right now. See Article #2229.

Microsoft SQL Server Community

SQL Server newsgroups (use NNTP, not HTML!)

Microsoft's SQL Server Home

SQL Server 2000 Operations Guide

Some of the TechNet Chat Transcripts might make interesting reading.

FAQ articles in the Knowledge Base

Resource / Reference Sites:

SQL Server FAQ

SQL Server Magazine

T-SQL Solutions

SQL Server Professional

SQLDev.Net

SQLXML.org

NT FAQ (SQL Server department)


SQL Server Central

Database Journal (formerly swynk.com/sql)

CallSQL

SQL.Nu

SQL Server Worldwide Users Group

Professional Association for SQL Server

SQL-Server-Performance.com

Resource sites by SQL Server MVPs:

Darren Green

Dinesh.T.K

Erland Sommarskog

Itzik Ben-Gan

Keith Kratochvil

Michael Hotek

Narayana Vyas Kondreddi

Umachandar Jayachandran

And these are the titles on my bookshelf. You might think I am exaggerating, but I took a picture.

Inside SQL Server 2000 (Delaney)


The Guru's Guide to SQL Server Stored Procedures, XML, and HTML (Henderson)

The Guru's Guide to Transact-SQL (Henderson)

Advanced Transact-SQL for SQL Server 2000 (Ben-Gan / Moreau)

SQL Server: Common Problems, Tested Solutions (Pike)

SQL Server 2000 Resource Kit (MS Press)

SQL Server 2000 Reference Library (Iseminger)

SQL Server 2000 Performance Tuning Technical Reference (MS Press)

Programming Microsoft SQL Server 2000 with Microsoft Visual Basic .NET

SQL for Smarties (Celko)

SQL Server 2000 Performance Optimization and Tuning Handbook (England)

SQL Server 2000 Optimization Guide (Fields)

Hitchhiker's Guide to Visual Basic and SQL Server, 6th ed. (Vaughn)

Writing Stored Procedures for Microsoft SQL Server (Shepker)


my bookshelf
How do I get the latest version of the JET OLEDB drivers?

597 requests - last updated Sunday, August 11, 2002

First, download the latest version of MDAC from http://www.microsoft.com/data/. Then, if you don't already have

JET 4.0 installed, download JET 4.0 SP3 FROM http://www.microsoft.com/data/download_Jet4SP3.htm (you will only

need this if you are running Windows 9x or NT 4.0, and do not already have JET components on your machine; if

you started clean with MDAC 2.6 or later -- see Q271908).

Then, get JET 4.0 SP6 from http://www.microsoft.com/downloads/release.asp?ReleaseID=38000.

You can see more information in Q239114 (Access 2000) and Q282010 (Access 2002 / Access XP).
Schema: how do I retrieve the description property of a column?

590 requests - last updated Friday, July 19, 2002

Assuming you created a column in Access and added a description to it, use the following:

<%
on error resume next
Set c = Server.CreateObject("ADOX.Catalog")
c.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=<path>\<file>.mdb"
d = c.Tables("<table>").Columns("<column>").Properties("Description").Value
Response.Write "Description = " & d
if err.number <> 0 then Response.Write "<" & err.description & ">"
Set c = nothing
%>

In SQL Server, let's say you created a column like this:

CREATE TABLE dbo.TestTable


(
id INT
)

Then you could add a description in the Enterprise Manager GUI, or you could use this code:
EXEC sp_addextendedproperty
'MS_Description',
'<Some description>',
'user',
dbo,
'table',
'T2',
'column',
id

Now, you can retrieve that value with the following code:

<%
set conn = Server.CreateObject("ADODB.Connection")
sql = "SELECT name,value FROM ::fn_listExtendedProperty " &_
"NULL, 'user', 'dbo', 'table', 'T2', 'column', 'id'"
set rs = conn.execute(sql)
if not rs.eof then
do while not rs.eof
response.write rs(0) & " = " & rs(1)
rs.movenext
loop
end if
%>

Note that the column name is surrounded in quotes when retrieving from the function, but not when it is passed to

the stored procedure.

To get the extended properties for ALL columns in the table, just change 'id' to default or NULL (without quotes).
How do I convert columns of values into a single list?

588 requests - last updated Tuesday, April 16, 2002

In Article #2248, we discussed a method for converting a comma-separated list (e.g. from a set of checkboxes, or a

multi-select list) into a column of values. What about the other way around? You could do it from ASP easily enough,

as follows:

<%
' assuming valid connection object 'conn'
Set rs = conn.execute("SELECT value FROM table")
if not rs.eof then
Do while not rs.eof
list = list & rs(0) & ","
rs.movenext
loop
Response.Write left(list,len(list)-1)
end if
%>

However, what if you wanted to do this totally within the confines of the database (e.g. called from another stored

procedure)? You could use the following method, however before proceeding you should read this note from UJ, as

well as Q287515:

SET NOCOUNT ON
SET CONCAT_NULL_YIELDS_NULL ON
DECLARE @list VARCHAR(8000)
DECLARE @delm VARCHAR(2)
SET @delm = ','

CREATE TABLE #TableToList (value INT)


INSERT #TableToList VALUES (14)
INSERT #TableToList VALUES (8)
INSERT #TableToList VALUES (12)
INSERT #TableToList VALUES (7)
SELECT @list = ISNULL(
@list + @delm + CAST(value AS VARCHAR(8000)),
CAST(value AS VARCHAR(8000))
)
FROM #TableToList
ORDER BY value

DROP TABLE #TableToList

SELECT list = @list


How do I limit the number of records returned in my resultset?

573 requests - last updated Thursday, June 27, 2002

Often you will have a huge resultset, but you only care about the first 5, 10 or 20 records. There are several

mechanisms to do this in both SQL Server and Access.

For these examples, let's assume you only want to retrieve the most recent 10 records from a table called foo, with

a datetime column called dt.

From the database side

For MS Access or SQL Server 7.0/2000, you can use TOP n:

SELECT TOP 5 column1, column2, dt FROM foo ORDER BY dt DESC

For SQL Server 6.5, you'll have to take a slightly different approach, utilizing the SET ROWCOUNT functionality (this

will not work in Access):

SET ROWCOUNT 5
SELECT column1, column2, dt FROM foo ORDER BY dt DESC
SET ROWCOUNT 0 -- (This last step is the most important!)

From the ASP side

You could do this from ASP in a couple of ways, though I really don't recommend either method. Reason being,

you're making the database do all the work of pumping records out of the database when you're not interested in all

of them.

The first method would simply be to maintain a counter, and when you get to your count, stop the loop.
<%
' ...
sql = "SELECT column1, column2, dt FROM foo ORDER BY dt DESC"
set rs = conn.execute(sql)
counter = 1
do while not rs.eof and counter < 5
' process rs(???)
counter = counter + 1
rs.movenext
loop
' ...
%>

Another method is to use the MaxRecords method of the ADODB.Recordset object. If you've been through

aspfaq.com in the past, you'll probably guess that this is my least favorite method.

<%
' ...
sql = "SELECT column1, column2, dt FROM foo ORDER BY dt DESC"
set rs = Server.CreateObject("ADODB.Recordset")
rs.MaxRecords = 5
rs.open sql, conn
do while not rs.eof
' process rs(???)
rs.movenext
loop
' ...
%>

Note that Access does not support the MaxRecords property (see Q186267), so this code sample would only be valid

for SQL Server.


What are the capacity specifications for Access, SQL Server, and MSDE?

556 requests - last updated Friday, August 16, 2002

Here are some guidelines for database size, object number and naming limitations contrasted across Access 2000/XP, SQL Server 7.0,

SQL Server 2000, and MSDE 2.0 (SQL Server 2000 Desktop Engine). The main reason for this chart was to answer the question "how

many rows can I get into a table?" -- which doesn't have a direct answer, but is limited by many of the variables listed below (not to

mention disk space!).

Parameter Access 2000/XP SQL Server 7.0 SQL Server 2000 MSDE 2.0

Number of instances per server n/a n/a 16 16

Number of databases per instance / server n/a 32,767 32,767 32,767

Number of objects per database 32,768 2,147,483,647 2,147,483,647 2,147,483,647

Overall size of database 2 GB 1 TB 1 TB1 2 GB

Number of columns per table 255 1024 1024 1024

Number of rows per table limited by storage limited by storage limited by storage limited by storage

Number of bytes per row


2 KB 8 KB 8 KB 8 KB
(Excluding TEXT/MEMO/IMAGE/OLE)

Number of columns per query 255 4,096 4,096 4,096

Number of tables per query 32 256 256 256

Size of procedure / query 64 KB 250 MB 250 MB 250 MB

Number of input params per procedure / query 1995 1,024 2,100 2,100

Size of SQL statement / batch 64 KB 64 KB 64 KB 64 KB

Depth of subquery nesting 50 32 32 32

Number of indexes per table 32 250 (1 clustered) 250 (1 clustered) 250 (1 clustered)

Number of columns per index 10 16 16 16

Number of characters per object name 64 128 128 128

Number of concurrent user connections 2552 32,7673 32,7673 54

1. Using a federated database in SQL Server 2000, you can have 32,767 databases of 1 TB each, which is probably more space

than anyone will ever need (though that phrase itself has proven dangerous to say <G>).

2. This is how many concurrent users Access will allow, however this number is much smaller when Access is used in a web-based
environment (see Article #2195).

3. SQL Server allows 32,767 concurrent connections, or the number of licenses allowed, whichever is lower.

4. MSDE has a performance throttler that kicks in when more than 5 workloads / batches are being run at once.

5. The Query Designer interface in Access limits you to 199 parameters, though it is possible (but not recommended) to create a

query with even more parameters.


How do I search for special characters (e.g. %) in SQL Server?

553 requests - last updated Wednesday, August 28, 2002

There are several characters that have special meaning within a SQL query, for example the percent sign (%) in a

LIKE query is a wildcard that essentially means "any number of characters can go here." Likewise, the underscore

(_) is a wildcard that says "any single character can go here." So what if you are actually looking for a value that

contains a literal percent sign? You will end up with bizarre results if you try the following:

SELECT columns FROM table WHERE


column LIKE '%%%'

Instead, you can try one of the following solutions:

SELECT columns FROM table WHERE


column LIKE '%[%]%'

-- or

SELECT columns FROM table WHERE


column LIKE '%\%%' ESCAPE '\'

The first query 'delimits' the special character with square brackets, telling the engine to treat it as a normal literal

character instead of a character with special meaning. The second query uses a custom escape character -- you can

use any character you like, just be careful that you aren't also expecting to use it as part of the literal string.

Now, you might be wondering, how do I escape a square bracket? If you have something like this:

SELECT columns FROM table WHERE


column LIKE '%[SQL Server Driver]%'

The results won't be what you expect, because an opening square bracket is considered a special character.

Surprisingly, you can avoid this problem in much the same way, by one of the following two queries:
SELECT columns FROM table WHERE
column LIKE '%[[]SQL Server Driver]%'

-- or

SELECT columns FROM table WHERE


column LIKE '%\[SQL Server Driver]%' ESCAPE '\'

You can do this replacement at the ASP side, before passing the string in, or within the SQL Server code itself.
Why does Enterprise Manager crash when I get an error in a stored procedure?

521 requests - last updated Tuesday, February 19, 2002

After installing Visual Studio.NET betas, you may find that when you are writing stored procedures and you

encounter an error, the EM crashes and aborts, rather than allowing you to correct the error.

There are a couple of workarounds. One is to use Query Analyzer or another tool (even ASP!) to create your stored

procedures. Another is to delete the 'OANACACHE' value from the Environment Variables section of system

properties. And finally, removing the .NET Beta and installing the RTM version will also fix this problem. You can find

about the final release at Microsoft's Visual Studio Home Page...


How do I protect my stored procedure code?

519 requests - last updated Thursday, July 25, 2002

When deploying applications to a client's server(s) or to a shared SQL Server, there is often a concern that other

people might peek at your business logic. Since often the code in a stored procedure can be proprietary, it is

understandable that we might want to protect our T-SQL work. There is a trivial way to do this in SQL Server,

instead of:

CREATE PROCEDURE dbo.foo AS


BEGIN
SELECT 'foo'
END

You can use the WITH ENCRYPTION option:

CREATE PROCEDURE dbo.foo WITH ENCRYPTION AS


BEGIN
SELECT 'foo'
END

Now, before you do this, make sure you keep the logic of the stored procedure in a safe place, since you won't have

easy access to the procedure's code once you've saved it.

Now you will notice that when you try to open the procedure in Enterprise Manager's GUI, you will receive the

following error:

Microsoft SQL-DMO
Error 20585: [SQL-DMO]
/******
Encrypted object is not transferable,
and script can not be generated.
******/
And when you try to use sp_helptext to review the code:

EXEC sp_helptext 'foo'

You will get the following error:

The object comments have been encrypted.

Unfortunately, there are at least two ways to defeat this mechanism. One is to run SQL Profiler while executing the

stored procedure; this often can reveal the text of the procedure itself, depending on what the stored procedure

does (e.g. if it has GO batches, dynamic SQL etc). The user can also delete the stored procedures, hook up SQL

Profiler, and ask you to re-create them (in which case they will capture the CREATE PROCEDURE statements).

Another is to use the code from this Planet Source Code article.
Why do I get 'Operation is not allowed when the object is closed' errors?

474 requests - last updated Friday, November 8, 2002

I think we've all seen this one before:

ADODB.Connection error '800a0e78'


Operation is not allowed when the object is closed.
/<file>.asp, line <line>

In older versions of MDAC, the error might be slightly different:

The operation requested by the application is not allowed if the object is closed.

This error can be caused when you try to access values from an empty recordset or from a recordset that has

already been closed. The most common cause, however, seems to stem from calling a stored procedure that does

not use SET NOCOUNT ON. See Article #2275 for more info.

A nearly identical, but far less common message:

ADODB.Recordset error '800a0e79'


Operation is not allowed when the object is open.
/<file>.asp, line <line>

This can be caused by trying to set a property that needs to be set before the object is opened. For example, trying

to set the MaxRecords property of an ADODB.Recordset object after opening the recordset:

<%
set rs = server.Createobject("ADODB.Recordset")
rs.open "SELECT columns FROM tablename",conn
rs.maxRecords = 5
%>
To fix, the code should be:

<%
set rs = server.Createobject("ADODB.Recordset")
rs.maxRecords = 5
rs.open "SELECT columns FROM tablename",conn
%>
Why do I get 80040E14 errors?

468 requests - last updated Monday, December 2, 2002

Microsoft OLE DB Provider for SQL Server error '80040e14'


Cannot insert the value NULL into column '<column>', table '<table>';
column does not allow nulls. INSERT fails. (or UPDATE fails.)

This error is pretty self-explanatory... if you try to insert/update a column that does not allow NULL values, with

NULL (e.g. by hard-coding NULL into the statement, or by leaving a column - that does not have a default value

defined - out of the INSERT list).

Microsoft OLE DB Provider for SQL Server error '80040e14'


Cannot resolve collation conflict for EQUAL TO operation.

or

Cannot resolve collation conflict for UNION operation.

This will happen if you are using #temp tables and the collation on tempdb does not match that of the database(s)

you're working in. The collation of all affected databases should match, or at least be compatible. You can run the

following command to compare collation between the databases (look at the Collation= section of the 'Status' value

in the first resultset):

EXEC sp_helpdb 'tempdb'


EXEC sp_helpdb '<userDatabaseName>'

If you can't change the collation settings for tempdb, you can override them for your #temp tables by creating your

definitions like this:


CREATE TABLE #temp
(
FullName VARCHAR(32) COLLATE DATABASE_DEFAULT
)

Microsoft OLE DB Provider for SQL Server error '80040e14'


Argument data type text is invalid for argument <n> of <function> function

Several string functions cannot be performed against TEXT/NTEXT columns. See Article #2061 for more

information.

Microsoft OLE DB Provider for SQL Server error '80040e14'


Invalid object name '<objectname>'.

If you know the object exists, and that you are in the correct database, this is usually a permissions issue. See

Article #2284.

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'


Syntax error (missing operator) in query expression '<expression>'

or

Syntax error in INSERT INTO statement

or

Syntax error in UPDATE statement

or
Syntax error in FROM clause

or

Syntax error in WHERE clause

or

Line <n>: Incorrect syntax near '<character>'.

You either used a reserved word as a column or alias name, or didn't delimit a value properly. See Article #2086 for

more information. In addition, the following error might occur:

Microsoft OLE DB Provider for SQL Server error '80040e14'


Syntax error or access violation

This could be for the same reasons, or it could be that you are using an ADODB.Command object and are attempting

to pass a string to an INT parameter or vice-versa. Try using the following approach instead of the troublesome

ADODB.Command object:

conn.execute("EXEC proc_name @param1='value'")

or

set rs = conn.execute("EXEC proc_name @param1='value'")

(This is both easier on the eyes and less prone to errors. If you need an output or return variable, consider changing

the stored procedure to send that back as a recordset instead - you can then use the nextRecordsSet() method to

process multiple resultsets independently.)

If you still get the same error, response.write the SQL statement and paste it into Query Analyzer. You might get a

more meaningful error message from the database.


If these suggestions don't solve the problem, response.write the bad query and post it to

microsoft.public.inetserver.asp.db and someone will try to help you figure out what the problem is.

Microsoft OLE DB Provider for ODBC Drivers (0x80040e14)


[Microsoft][ODBC Microsoft Access Driver] The changes you requested to the
table were not successful because they would create duplicate values in the
index, primary key, or relationship. Change the data in the field or fields
that contain duplicate data, remove the index, or redefine the index to
permit duplicate entries and try again.

You will need to investigate your query, and the structure of the table(s) it affects, to determine why you will be

violating one of these relationships.

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'


Undefined function 'NZ' in expression.

Several functions available within Access are not available through ADO/JET providers - see Article #2394.

Microsoft OLE DB Provider for SQL Server error '80040e14'


SQL Web Assistant: Could not open the output file.

This is usually caused when you are using a web task to modify existing HTML files which are also in use by IIS. One

workaround would be to cycle between two filenames... active and inactive. Flip which one is 'current' every time the

web task runs; depending on the frequency of the web task, this will reduce the chance that someone will still have

the inactive file open when you make the other file active. In addition, you could delete the inactive file after each

run of the web task, to make it even more unlikely that IIS will have a lock on the file.
This can also happen if the account that the SQL Server and SQL Server Agent services don't have sufficient

privileges on the folder where the web task outputs its file.

Microsoft OLE DB Provider for SQL Server error '80040e14'


Cannot create a row of size <n> which is greater than the
allowable maximum of 8060.

This can happen if you have a table that is defined to allow more than 8060 characters per row (SQL Server warns

you about this when creating the table, but allows you to create it nonetheless). This kind of structure can be useful

if, say, you have two different VARCHAR(8000) columns where only one of them could possibly contain that much

text. If you try to insert 8000 characters into both columns, you get the above error. Your SQL statements need to

be constructed with logic that carefully insulates them from exceeding the physical bounds of the table. If you feel

you might need to exceed 8060 characters in a single row, consider storing the characters off-row (e.g. in a

TEXT/NTEXT column).

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'


Could not allocate space for object '<object>' in database
'<database>' because the 'PRIMARY' filegroup is full.

This error happens for one of two reasons. Either the disk where the data is stored is full, or the database is not set

to auto-grow and it has reached capacity. If the former, you will need to free up space on the drive (or move the

data files to a different location). If the latter, you will need to set the database to auto-grow, or clear out stale data

and perform a shrink.


Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
Could not insert a row larger than the page size into a hash
table. Resubmit the query with the ROBUST PLAN hint.

or

Cannot create a worktable row larger than allowable maximum.


Resubmit your query with the ROBUST PLAN hint.

or

The current query would generate a key size of <n> for a work table. This
exceeds the maximum allowable limit of 900.

This usually means you are trying to run a complex query with a row width that the optimizer can't handle (typically

due ot use of wide CHAR or VARCHAR columns). In SQL Server 7.0 and up, you can solve this issue by adding

OPTION ROBUST PLAN to your query. Here is a Books Online excerpt about ROBUST PLAN:

Forces the query optimizer to attempt a plan that works for the maximum potential row size,

possibly at the expense of performance. When the query is processed, intermediate tables and

operators may need to store and process rows that are wider than any of the input rows. The rows

may be so wide that, in some cases, the particular operator cannot process the row. If this

happens, SQL Server produces an error during query execution. By using ROBUST PLAN, you

instruct the query optimizer not to consider any query plans that may encounter this problem.

For more information about how to implement OPTION ROBUST PLAN, see the 'SELECT' topic in Books Online.

Of course, this could lead to the following error:

Microsoft OLE DB Provider for SQL Server error '80040e14'


Warning: The query processor could not produce a query plan
from the optimizer because the total length of all the columns
in the GROUP BY or ORDER BY clause exceeds 8000 bytes. Resubmit
your query without the ROBUST PLAN hint.
If this is the case, post your schema, sample data, current query and desired output to

microsoft.public.sqlserver.programming and you will get some help.

Microsoft OLE DB Provider for SQL Server (0x80040E14)


Invalid column name 'value'with_apostrophe'.

This is usually caused by using " instead of ' to delimit string values, often in an attempt to avoid having to replace '

with '' (see Article #2035). However, " are not string delimiters by default in SQL Server, they are identifiers. This

means that strings inside of " within a SQL expression are expected to contain column names. So, instead of:

SQL = "UPDATE table SET column = """ & request.form("value") & """"

use:

SQL = "UPDATE table SET column = '" & _


replace(request.form("value"),"'","''") & "'"

Further to this, two other comments. First, the act of doubling up the ' character is not only to prevent parsing

errors, but also to avoid exposure to your system to attempts at SQL injection. Second, if you override the quoted

identifiers so that " can be interpreted as a string delimiter, instead of worrying about ', now you have to worry

about " in the value...


What does "ambiguous column name" mean?

463 requests - last updated Thursday, February 21, 2002

This usually means your SQL statement is joining on more than one table, and doesn't know which column you're

trying to retrieve. For example:

SELECT column1
FROM table1
JOIN table2
ON table1.column1 = table2.column1

To fix this, you'll want to butter up the statement a bit:

SELECT column1 = t1.column1


FROM table1 t1
JOIN table2 t2
ON t1.column1 = t2.column1

The changes:

■ Used an alias for the column name, so that external code continues to work when referencing "column1"

■ Added the prefix "t1" to the requested column, so that the engine knows which table we want the data from

■ Added table aliases in the FROM / JOIN clauses to shorten the code
Why is Query Analyzer only returning 255 characters of my VARCHAR / TEXT column?

453 requests - last updated Monday, July 22, 2002

SQL Server's Query Analyzer tool is limited, by default, to display 255 characters of any column (regardless of

datatype).

SQL Server 2000

To expand this a bit, you can go to QA's Tools / Options menu, move to the results tab, and adjust the 'Maximum

characters per column' field. The maximum length of the output for any column is 8,192 characters; if you need

more, consider another application to view the data.

SQL Server 7.0

Go to the Query menu, Current Connection Options. On the Advanced tab, change the value for 'Maximum

characters per column.'


How do I deal with multiple resultsets from a stored procedure?

452 requests - last updated Monday, July 8, 2002

If you have a stored procedure that looks like this:

CREATE PROCEDURE myProc AS


BEGIN
SELECT columns FROM table1

SELECT columns FROM table2

SELECT columns FROM table3


END

You might be confused about how to retrieve the second and third set of results when using a standard rs against

the connection object. Here is code that demonstrates how to handle this, using the NextRecordSet() method:

<%
' ...
' assuming valid and open object, conn

set rs = conn.execute("EXEC myProc")

' process first resultset

if not rs.eof then


do while not rs.eof
response.write rs(0)
rs.movenext
loop
end if

' move to second resultset, using nextRecordSet()

set rs = rs.nextRecordSet()
if not rs.eof then
do while not rs.eof
response.write rs(0)
rs.movenext
loop
end if

' move to third resultset, using nextRecordSet()

set rs = rs.nextRecordSet()
if not rs.eof then
do while not rs.eof
response.write rs(0)
rs.movenext
loop
end if
rs.close: set rs = nothing
' ...
%>
Why can't I use LIKE '%datepart%' queries for dates against SQL Server?

451 requests - last updated Monday, July 1, 2002

Several people want to offer the ability for users to enter any portion of the date (e.g. only the month, or only the

year), and leave wildcards for the rest. So, for all records in 2002, they would do something like this:

SELECT columns FROM table WHERE dateColumn LIKE '%/2002 %'

Unfortunately, SQL Server does not internally store dates in the character format you see them represented as in

client tools like Enterprise Manager or Query Analyzer.

While for some odd reason Books Online recommends LIKE queries against datetime values, date range searches are

much more effective because they don't rely on specific formatting of dates, regional settings on a server, etc. They

will also have the ability to use an index, if it exists. Here is the above query in a much more efficient and logical

format:

SELECT columns FROM table WHERE


dateColumn >= '20020101'
AND dateColumn < '20030101'

Another interesting approach, albeit not as efficient as the above, is to use the shorthand datepart functions in SQL

Server to match one or more criteria passed in. The following stored procedure will work for any combination of day,

month and year:


CREATE PROCEDURE dbo.ReturnRecordsForAnyPartOfDate
@year INT = 0,
@month INT = 0,
@day INT = 0
AS
BEGIN
SELECT columns FROM table
WHERE
YEAR(dateColumn) = CASE
WHEN @year > 0 THEN
@year
ELSE
YEAR(dateColumn)
END
AND
MONTH(dateColumn) = CASE
WHEN @month > 0 THEN
@month
ELSE
MONTH(dateColumn)
END
AND
DAY(dateColumn) = CASE
WHEN @day > 0 THEN
@day
ELSE
DAY(dateColumn)
END
END

And you could call this from ASP as follows:


<%
if request.form("y") <> "" then
y = clng(request.form("y"))
m = clng(request.form("m"))
d = clng(request.form("d"))
end if
%>

<form method=post name=dateSearch>

Year:

<select name=y>
<option value=0>Any
<%
for i = 2002 to 2000 step -1
response.write "<option value=" & i
if i = y then response.write " SELECTED"
response.write ">" & i
next
%>
</select>

Month:

<select name=m>
<option value=0>Any
<%
for i = 1 to 12
response.write "<option value=" & i
if i = m then response.write " SELECTED"
response.write ">" & monthname(i)
next
%>
</select>

Day:

<select name=d>
<option value=0>Any
<%
for i = 1 to 31
response.write "<option value=" & i
if i = d then response.write " SELECTED"
response.write ">" & i
next
%>
</select>

<input type=submit>

</form>

<hr size=1>

<%
if request.form("y") <> "" then
set Conn = Server.CreateObject("ADODB.Connection")
Conn.open "<connection_string>"
sql = "EXEC dbo.ReturnRecordsForAnyPartOfDate " & _
y & ", " & m & ", " & d
set rs = conn.execute(sql)
if not rs.eof then
do while not rs.eof
response.write rs(0) & "<br>"
rs.movenext
loop
else
response.write "No records."
end if
rs.close: set rs = nothing
conn.close: set conn = nothing
end if
%>
Can I have optional parameters to my stored procedures?

414 requests - last updated Monday, August 26, 2002

Yes, you can have a stored procedure that has optional parameters. You can do this by setting a default value for

each parameter that you want to make optional. The default value is typically NULL, but it doesn't have to be. Here

is some sample code, ready to test in Query Analyzer:

CREATE PROCEDURE foo


@param1 VARCHAR(32) = NULL,
@param2 INT = NULL
AS
BEGIN
PRINT '-------------------------'
PRINT COALESCE
(
'@param1 = '+@param1,
'@param1 was empty'
)
PRINT COALESCE
(
'@param2 = '+CAST(@param2 AS CHAR(9)),
'@param2 was empty'
)

/*

-- sample usage in a query

SELECT columns FROM table WHERE


(@param1 IS NULL OR column1 = @param1)
AND
(@param2 IS NULL OR column2 = @param2)
*/

END
GO
EXEC foo @param1='bar', @param2=4
EXEC foo @param1='bar'
EXEC foo @param2=4
EXEC foo 'bar',4
EXEC foo 'bar'

EXEC foo

For an Access stored query, you can use the following logic:

PARAMETERS Param1 VARCHAR(32), Param2 INT;


SELECT columns FROM table WHERE
([Param1] IS NULL OR column1 = [Param1])
AND
([Param2] IS NULL OR column2 = [Param2])
Why do I get 800a0cb3 errors?

403 requests - last updated Thursday, October 17, 2002

There are several text messages that go along with 0x800a0cb3 errors:

Object or provider is not capable of performing requested operation.

The operation requested by the application is not supported by the provider.

Current Recordset does not support updating. This may be a limitation of the provider, or of
the selected locktype.

Unknown runtime error.

Depending on which error message you are receiving, one of the following is probably true:

■ you are using named constants (e.g. adUseClient) but forgot to include ADOVBS.INC (see Article #2102 and

Article #2112)

■ you tried to use the NextRecordset() method to process multiple resultsets from an Access provider (see

Q202433)

■ you tried to combine server-side recordset properties, e.g. cachesize, with a client-side (adUseClient)

recordset - make sure the properties you are using make sense for the type of recordset you've opened

■ you tried to use advanced methods or properties such as bookmark, MovePrevious, or AbsolutePage on

default recordset objects - make sure you set the correct lockType and/or cursorLocation

■ you are trying to use an ADODB.Recordset object with an improper lock type to handle an update or addnew

- use an UPDATE or INSERT statement instead of a Recordset object (see Article #2191).

■ you are using an ADOX.Catalog object (or one of several other potential objects), and tried to close it. All

you have to do is set a catalog object to nothing... it does not have a close() method.
What datatype should I use for my character-based database columns?

395 requests - last updated Thursday, October 17, 2002

Often I see the phrase "I have an NVARCHAR column..." and sometimes have to keep myself from asking "why did

you choose that datatype?" Most times, this datatype isn't chosen intentionally; when you upsize from Access to SQL

Server, or transfer from other database products such as Sybase SQL Anywhere, this is the default datatype applied

to character-based columns (possibly to ensure that any Unicode data stored in such columns would not be lost /

corrupted).

When designing your database, you should really try to understand your data, and the datatype that suits it best.

Here is an outline of the datatypes available for character-based data:

CHAR supports fixed width strings, up to 8,000 characters. Best used when data length is constant,
CHAR
e.g. social security numbers or ISBN numbers.

Similar to CHAR, with the support of Unicode characters. You should only use this datatype if you

NCHAR need Unicode support - due to storage overhead (2 bytes per character means the maximum

length of your string is 4,000 characters).

VARCHAR supports variable length strings, up to 8,000 characters. Best used when data length is
VARCHAR
variable, e.g. last names or product SKU codes.

Similar to VARCHAR, with the support of Unicode characters. You should only use this datatype if

NVARCHAR you need Unicode support - due to storage overhead (2 bytes per character means the maximum

length of your string is 4,000 characters).

TEXT supports variable length strings, up to 2 GB, stored off row with a 16-byte pointer in the

record itself. Should be used whenever your data will exceed the 8,000 character limit of

TEXT CHAR/VARCHAR columns (though if you need only 12,000 or even 24,000 characters, you might

consider overflowing into multiple VARCHAR columns, rather than use the inflexible and less

efficient TEXT datatype).

Similar to TEXT, with the support of Unicode characters. You should only use this datatype if you

NTEXT need Unicode support - due to storage overhead (2 bytes per character means the maximum

length of your string is only 1 GB).


Of course, choosing TEXT/NTEXT in MSDE limits your options somewhat - if you plan to store 1 or 2 GB in these

columns, you can only store a couple at most before exceeding the capacity of the entire database (see Article

#2345 for more information).

So, if you inherited nchar/nvarchar/ntext columns from an upsize or import, consider changing those that do not

need to support Unicode characters to their non-Unicode datatype equivalents. If you do need to support Unicode

strings, make sure you use an N prefix (see Q239530 for more information):

INSERT fakeTable(nCharColumn) VALUES(N'foo')

Access

Access only supports TEXT (255) and MEMO (64 KB when entered through the GUI, and 1 GB when entered

programmatically), so the choice here is much easier - use TEXT unless you need more than 255 characters. In

addition, if you only need 10 or 20 characters, don't accept the default size (50) as this will be a considerable waste

of space.
Why do I get 800A0E7D errors?

387 requests - last updated Tuesday, September 17, 2002

When using the command object to invoke a SQL Server stored procedure, you may have seen this error:

ADODB.Command (0x800A0E7D)
Requested operation requires an OLE DB Session object, which is not
supported by the current provider.

or

ADODB.Command error '800a0e7d'


The connection cannot be used to perform this operation. It is either closed
or invalid in this context.

The most likely cause is that you are trying to set the command object's activeConnection to an invalid connection

object, or you didn't set the activeConnection property at all. Check over your code and make sure your connection

object is valid and open before trying to set it as the active connection for your command object. Or avoid using the

command object altogether; it is unnecessary for most SP executions from ASP.

Other possible causes include:

■ you are using SQLOLEDB and have forcefully turned off pooling (either in the metabase or by adding "OLE

DB Services=-2" to the connection string);

■ you are using a File DSN which IUSR cannot access;

■ you lack permissions to connect to the SQL Server;

■ you haven't properly defined your ADODB.Command parameters;

■ you are using an ADODB.Recordset when you could be getting by with a simpler object (see Article #2191);

or,

■ you are using an ancient version of MDAC (upgrade to the most recent version at

http://www.microsoft.com/data/).
Why do I get 'object could not be found' or 'invalid object name', when the object exists?

382 requests - last updated Tuesday, April 16, 2002

You may have seen one of these errors:

Server: Msg 2812, Level 16, State 62, Line 1


Could not find stored procedure '<objectname>'.

-- or

Server: Msg 208, Level 16, State 1, Line 1


Invalid object name '<objectname>'.

This can can happen in SQL Server, and can be due to a number of reasons. For example:

■ the user you are connecting as does not have SELECT, UPDATE, INSERT, DELETE, EXEC permissions on the

object;

■ you aren't logging in / connecting as the user you expect;

■ you are referencing the object without an owner name prefix (or with the wrong owner name);

■ you are connected to the wrong database;

■ you are, in fact, spelling the object's name incorrectly.


What are the limitations of MSDE?

367 requests - last updated Friday, November 8, 2002

All SQL Server 2000 versions (as well as several other Microsoft products) ship with SQL Server 2000 Desktop

Edition, often referred to as MSDE (Microsoft Data Engine) 2.0. Essentially, MSDE is a version of SQL Server that you

can distribute with applications. As such, it is slightly lighter weight, and has some notable restrictions. Other than

the following list, however, MSDE and the other editions of SQL Server have very similar behavior and performance

characteristics (see Article #2345 for a technical comparison).

■ an MSDE database can be no larger than 2 GB;

■ performance throttling occurs when there are more than five (5) concurrent workload batches in progress;

■ MSDE cannot be a publisher in transaction replication, and when acting as publisher in all other types of

replication, must also be the distributor;

■ MSDE does not ship with GUI administrative tools such as Enterprise Manager or Query Analyzer. To

administer an MSDE database, you can either install client tools from any other version of SQL Server, use a

programming environment like Visual Studio or Web Matrix, or use OSQL from the command line (see

Q241397 for an example);

■ MSDE does not offer OLAP / data warehousing capabilities; and,

■ MSDE does not come with Books Online, but you can download it from Microsoft at

http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp

The licensing issues surrounding MSDE have long needed to be clarified - perhaps

http://www.microsoft.com/sql/howtobuy/msdeuse.asp is a good start.


Why do I get 80040E21 errors?

366 requests - last updated Tuesday, September 17, 2002

Microsoft OLE DB Provider for ODBC Drivers error '80040e21'


ODBC driver does not support the requested properties.

This can happen if you use an adOpenStatic ADODB.Recordset object to open Excel, or use an adOpenKeyset

recordset to perform paging,

Basically, make sure the methods/properties you are using are supported by the driver/provider you are using to

access your data.

If you are executing a stored procedure, make sure you do so through the connection.execute() method, rather than

a command object, and add the following two lines of code to the beginning of your procedure:

SET ANSINULL OFF


SET NOCOUNT ON

If you are using With, an ADODB.Recordset, and the AddNew/Update methodology, consider not doing so (see

Article #2191) and, failing that, see Q228935.

If you are sure that these things are intact, make sure you have the latest version of MDAC

(http://www.microsoft.com/data/).
Microsoft OLE DB Provider for SQL Server error '80040e21'
Optional feature not implemented.

or

ADOBD Parameter error '800a0d5d'


The application is using a value of the wrong type for
the current operation.

This can often be caused by using invalid ad* constants with ADODB.Command, such as adDBDate. Use

adDBTimeStamp instead (see Q214459) or, better yet, use a straight EXEC statement instead of using the

command object at all (see Article #2201).

Microsoft JET Database Engine error '80040e21'


Cannot include Memo, OLE, or Hyperlink Object when you select unique values (<column(s)>).

This error is pretty explanatory. Access does not allow these 'special' columns to be included in DISTINCT queries.

So if you have the following code, where bar is a memo column:

SELECT DISTINCT foo, bar FROM table

You could change it to the following to alleviate the error:

SELECT foo, bar FROM table GROUP BY foo, bar

Though I have always questioned why you would have duplicates in a memo column; since it is designed to store a

LOT of text, it seems that in most scenarios it would be unlikely to repeat enough, and also be insignificant in terms

of individual records?
Microsoft OLE DB Provider for SQL Server error '80040e21'
Invalid character value for cast specification.

This can happen if you try and pass a NULL string or a non-string datatype to an ADODB.Recordset object or a

stored procedure, and the database logic attempts to perform an implict or explicit CONVERT or CAST.

Microsoft JET Database Engine error '80040e21'


You tried to execute a query that does not include the specified expression
'<column>' as part of an aggregate function.

If you are using an aggregate function (e.g. SUM, COUNT, MAX), then any other column in the SELECT list must also

be in the GROUP BY clause. This is so that the database knows how to organize results.

Microsoft OLE DB Provider for ODBC Drivers (0x80040E21)


Multiple-step OLE DB operation generated errors. Check each OLE
DB status value, if available. No work was done.

For full information on this error message, see Article #2288.


Which database should I use for my ASP application?

354 requests - last updated Friday, November 8, 2002

This is not an easy question to answer, as there are many criteria which can vary in presence and rank from shop to

shop. Personally, and in a general sense, I do have favorites.

For Enterprise level applications, my preference goes:

Microsoft SQL Server

IBM DB2 Universal

Oracle 9i

For small business applications and/or where cost is an issue and size/concurrency is expected to be limited, my

preference goes:

Microsoft SQL Server MSDE

MySQL

Microsoft Access

Here is where your work comes in. You can evaluate each product at the following URLs, and determine which

suit(s) your needs and budget the best.

Microsoft SQL Server

Microsoft SQL Server - MSDE

MySQL

IBM DB2 Universal

IBM DB2 Everyplace

IBM Informix Dynamic Server (IDS)

IBM Informix Online5


IBM Informix SE

Sybase Adaptive Server Enterprise

Sybase SQL Anywhere

Sybase Adaptive Server IQ Multiplex

Oracle 9i

Microsoft Visual FoxPro

Microsoft Access
Why do I get 80040200 / 80040514 / 800A0E7A errors?

342 requests - last updated Friday, September 27, 2002

If you are getting one of the following errors:

ADO could not find the specified provider.

Provider cannot be found. It may not be properly installed.

Data source name not found and no default driver specified.

Unspecified runtime error.

Library not registered.

Class not registered.

First things first: check your connection string. This error can often happen if you have a typo in your provider or

driver details.

If you are sure that your connection string is accurate, then follow these directions, depending on the database

platform in use:

SQL Server

Make sure you have the most recent version of MDAC installed (http://www.microsoft.com/data/).

Access

This is often caused by attempting to connect to an Access database using OLE-DB (e.g.

Provider=Microsoft.Jet.OLEDB.4.0;) without having JET components installed. A major source of this problem is that

Microsoft stopped shipping the JET files with MDAC, starting at 2.6 (see Q271908), so many machines set up

starting at that baseline or later do not have the necessary JET runtime files.

See Article #2342 for information on getting the most recent version of the JET provider.
Other problems with Access include fat-fingering the connection string, or mixing up Access and JET parameters,

e.g:

Driver={Micrsoft Axess Diver (*.mdb)};DBQ=c:\file.mdb


(several misspelled words)

Driver={Microsoft Access Driver (*.mdb)};Data Source=c:\file.mdb


(DBQ is the parameter for database location with the Access driver)

Provider=Microsoft.Jet.OLEDB.4.0;DBQ=c:\file.mdb
(Data Source is the parameter for database location with the JET provider)

See Article #2126 for sample Access connection strings to help model your own.

Any other vendor

Make sure you have the most recent version of MDAC installed (http://www.microsoft.com/data/), and check the

vendors' web site and make sure your server has the most recent version of their ODBC driver or OLE-DB provider.

Under certain scenarios, connection pooling may cause the following error:

Microsoft OLE DB Provider for ODBC Drivers error '80040200'


[Microsoft][ODBC Driver Manager] Only SQL_DRIVER_NOPROMPT is allowed when connection pooling
is enabled

To make this error go away, you can override the prompt property of the connection object in the following

manner:
<%
const adPromptNever = 4
set conn = server.createObject
conn.Properties("Prompt") = adPromptNever
conn.open <connection string>
...
%>

For more information, see Q167745, Q200886 and Q253114.

If you are using any of the fancy wizards that come with Visual Studio 6.0, you may have this error message:

Supplied provider is different from the one already in use

My advice: don't use DataGrid, DataEnvironment, ADODC, DTC or any other 'automatic' / helper control, or at least

don't try to combine provider=MS.DataShape with data provider=Microsoft.Jet.OLEDB.4.0.

And finally, if you are using CDO / CDONTS with (or trying to replicate) Outlook Web Access, you may get one of the

following:

MAPI_E_END_OF_SESSION

CdoE_END_OF_SESSION

You may also see Event ID 4096 in the event log.

If you see any of the above, please refer to Q297969 for information and potential resolution(s).
How do I determine if a column exists in a given table?

323 requests - last updated Saturday, August 10, 2002

In Access, we can take one of our existing schema extractors and modify it slightly:

<%
columnToFind = "foo"
dbname = "/path_to.mdb"
tablename = "tablename"

ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="


ConnStr = ConnStr & server.mappath(dbname)

set adoxConn = Server.CreateObject("ADOX.Catalog")


set adodbConn = Server.CreateObject("ADODB.Connection")
adodbConn.open ConnStr
adoxConn.activeConnection = adodbConn
set table = adoxConn.Tables(tablename)
found = false
for each col in table.columns
if lcase(col.name) = lcase(columnToFind) then
found = true
exit for
end if
next
set table = nothing
adodbConn.close: set adodbConn = nothing
set adoxConn = nothing

if found then
response.write("Column exists.")
else
response.write("Column does not exist.")
end if
%>
In SQL Server, you can use a much more direct approach:

<%
columnToFind = "foo"
dbname = "dbname"
tablename = "tablename"

connStr = "Provider=SQLOLEDB;Server=x.x.x.x;Database=" & _


dbname & ";UID=username;PWD=password"

set conn = Server.CreateObject("ADODB.Connection")


conn.open connStr

SQL = "SELECT COALESCE(COL_LENGTH('" & tablename & "'," & _


"'" & columnToFind & "'),0)"

set rs = conn.execute(sql)

if clng(rs(0))>0 then
response.write("Column exists.")
else
response.write("Column does not exist.")
end if

rs.close: set rs = nothing


conn.close: set conn = nothing
%>
Why do I get 800a01fb errors?

320 requests - last updated Wednesday, July 17, 2002

Here are the situations I know of that can bring about 800a01fb errors.

Microsoft VBScript runtime error '800a01fb'


An exception occurred: 'Open'

This error can happen when you use an ADODB.Recordset to retrieve a resultset, and the resultset is empty.

Switching from ADODB.Recordset to conn.execute(sql) seems to alleviate this problem.

Microsoft VBScript runtime error '800a01fb'


An exception occurred: 'Server.CreateObject'

If you are attempting to create an ADODB.Connection, ADODB.Recordset, ADODB.Stream or ADODB.Command

object, this error might be caused by a corrupted MDAC. Update MDAC from http://www.microsoft.com/data/.

Microsoft VBScript runtime error '800a01fb'


An exception occurred: 'MethodForYourCOMObject'

If you try to pass an array to a COM object, you might see this error. See Q217114 for information on passing

arrays as arguments to VB COM objects.


How do I start SQL Server Agent from ASP?

312 requests - last updated Wednesday, August 14, 2002

When you rely on jobs in SQL Server, it can be quite frustrating when SQL Server Agent has stopped and either it's

not set to auto-restart, or you are using an older version of SQL Server where this option didn't exist. So if you can't

connect to the database using Enterprise Manager or Query Analyzer, you can write up a quick ASP script that

connects to the database and starts the service (assuming the SQL Server service is started, and that the user has

appropriate permissions to execute such a command):

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connectionString>"

sql = "EXEC Master.DBO.XP_ServiceControl 'QUERYSTATE','SQLServerAgent'"


set rs = conn.execute(sql)
if trim(rs(0)) <> "Running." then
response.write "Starting SQL Server Agent..."
sql = "EXEC Master.DBO.XP_ServiceControl 'START', 'SQLServerAgent'"
conn.execute(sql)
else
response.write "SQL Server Agent already started..."
end if

conn.close
set conn = nothing
%>

Keep in mind that XP_ServiceControl is undocumented and unsupported; its behavior could change with a service

pack or the next release of SQL Server.


How do I handle alphabetic pagination?

310 requests - last updated Saturday, August 10, 2002

The following will allow you to query a table for last names beginning with each letter in the alphabet.

<%
Ltr = UCase(Request.QueryString("Ltr"))
if (Ltr < "A") or (Ltr > "Z") then Ltr = "A"
for i = 65 to 90
if i > 65 then response.write " | "
if Asc(Ltr) <> i then
response.write "<a href='Alpha.asp?Ltr=" & _
Chr(i) & "'>" & Chr(i) & "</a>"
else
response.write "<b>" & Chr(i) & "</b>"
end if
next
SQL = "SELECT lastName FROM nameTable WHERE lastName LIKE '" & Ltr & "%'"
Response.Write "<p>" & SQL

' ... execute query and process results


%>

Now, you might only be interested in letters (and other characters) that are actually represented in your database.

Most of you probably don't have last names that start with Q or X, for example; and sometimes a user will enter

their name incorrectly, e.g. !Smith. So you could consider building your alphabet list like this:
<%
Ltr = UCase(Request.QueryString("Ltr"))
Set Conn = Server.CreateObject("ADODB.Connection")
conn.open "<connectionString>"
SQL = "SELECT DISTINCT LEFT(lastName,1) FROM nameTable ORDER BY LEFT(lastName,1)"
set rs = conn.execute(SQL)
do while not rs.eof
response.write " | "
rs0 = UCase(rs(0))
if rs0 <> Ltr then
response.write "<a href='Alpha.asp?Ltr=" & _
rs0 & "'>" & rs0 & "</a>"
else
response.write "<b>" & rs0 & "</b>"
end if
rs.movenext
loop
response.write " |"

SQL = "SELECT lastName FROM nameTable WHERE lastName LIKE '" & Ltr & "%'"
Response.Write "<p>" & SQL

' ... execute query and process results


%>
How do I determine if a database exists?

307 requests - last updated Saturday, August 10, 2002

With Access, you would simply use FileSystemObject's FileExists method to verify the location of the MDB file. e.g.:

<%
databaseName = "databaseName"
set fso = Server.CreateObject("Scripting.FileSystemObject")
if fso.fileExists(server.MapPath("/" & databaseName & ".mdb")) then
response.write "Database exists."
else
response.write "Database does not exist."
end if
set fso = nothing
%>

With SQL Server, you can run the following query:

<%
databaseName = "databaseName"

connStr = "Provider=SQLOLEDB;Server=x.x.x.x;Database=" & _


dbname & ";UID=username;PWD=password"

set conn = Server.CreateObject("ADODB.Connection")


conn.open connStr

SQL = "SELECT COALESCE(DB_ID('" & databaseName & "',0)"

set rs = conn.execute(sql)

if clng(rs(0))>0 then
response.write("Database exists.")
else
response.write("Database does not exist.")
end if
rs.close: set rs = nothing
conn.close: set conn = nothing
%>
How do I determine if a table exists in the database?

301 requests - last updated Saturday, August 10, 2002

In Access, we can take one of our existing schema extractors and modify it slightly:

<%
tableToFind = "foo"
dbname = "/file.mdb"

ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;data source="


ConnStr = ConnStr & server.mappath(dbname)

set adoxConn = Server.CreateObject("ADOX.Catalog")


set adodbConn = Server.CreateObject("ADODB.Connection")
adodbConn.open ConnStr
adoxConn.activeConnection = adodbConn
found = false
for each table in adoxConn.tables
if lcase(table.name) = lcase(tableToFind) then
found = true
exit for
end if
next
adodbConn.close: set adodbConn = nothing
set adoxConn = nothing

if found then
response.write("Table exists.")
else
response.write("Table does not exist.")
end if
%>

In SQL Server, you can use a much more direct approach:


<%
tableToFind = "foo"
dbname = "dbname"

connStr = "Provider=SQLOLEDB;Server=x.x.x.x;Database=" & _


dbname & ";UID=username;PWD=password"

set conn = Server.CreateObject("ADODB.Connection")


conn.open connStr

SQL = "SELECT COALESCE(OBJECT_ID('" & tablename & "'," & _


"'U'),0)"

set rs = conn.execute(sql)

if clng(rs(0))>0 then
response.write("Table exists.")
else
response.write("Table does not exist.")
end if

rs.close: set rs = nothing


conn.close: set conn = nothing
%>
How do I solve 'Cannot open a database created with a previous version...' errors?

298 requests - last updated Tuesday, April 30, 2002

When working with an Access 97 database, you may have seen this:

Cannot open a database created with a previous version of your application

This usually occurs when moving the database to a machine that doesn't have Jet 3.5 installed (or properly

registered). One workaround is to upgrade the machine in question to Access 2000 or greater, and make sure you

have MDAC 2.7 installed (which you can download from http://www.microsoft.com/data/).

Failing that:

Go to Start / Run, type regedit and navigate to the following branch:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\ISAM Formats

Right-click ISAM Formats and choose New > Key.

Name the key Jet 3.x and create the following values:

Value Name Type Data

CreateDBOnExport REG_BINARY 00

Engine REG_SZ Jet 3.x

IndexDialog REG_BINARY 00

IsamType REG_DWORD 0x00000000 (0)

OneTablePerFile REG_BINARY 00
(See Q310804 for more information.)

Next, find msrd3x40.dll, copy the path to this file, and again go to Start / Run and type:

regsvr32 <path>\msrd3x40.dll
Can I use the NZ() function without getting 80040E14 errors?

290 requests - last updated Monday, August 26, 2002

VBScript doesn't support the NZ() function. But here is a custom implementation, and a demonstration of its use:

<%
Function NZ(ValueIfNotNull, ValueIfNull)
NZ = ValueIfNotNull
If (IsNull(NZ)) Then NZ = ValueIfNull
End Function

str1 = "foo"
Response.Write NZ(str1, "bar")

str2 = NULL
Response.Write NZ(str2, "bar")
%>

A more common place you would expect to use NZ(), however, is returning the results of an Access query. You will

find that if you try to use NZ() and call the query from an ASP page, you'll get this error:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'


[Microsoft][ODBC Microsoft Access Driver] Undefined function 'NZ' in expression.

Let's say you have a numeric column, and want it to return 0 instead of NULL for all records that are NULL.

In Access, you would say:

SELECT ColumnValue =
NZ(Column, 0)
FROM table ...

Since you can't use NZ() from ODBC/OLE-DB, you can use IIF instead:
SELECT ColumnValue =
IIF(ISNULL([Column]), 0, [Column])
FROM table ...

Or you can try IFNULL if you are going through ODBC, e.g.

SELECT ColumnValue =
IFNULL(Column, 0)
FROM table ...

If you are porting this code to SQL Server, you would use slightly different syntax:

SELECT ColumnValue =
COALESCE(Column, 0)
FROM table ...

or

SELECT ColumnValue =
ISNULL(Column, 0)
FROM table ...

(I prefer the COALESCE expression, which accepts multiple successive arguments. So you could say COALESCE(col1,

col2, col3, ...) and it would return the *first* column that is NOT NULL.)

Of course if NULL means the same thing as 0, you should consider running this query:

UPDATE column SET column = 0 WHERE column IS NULL

For more information on preventing NULLs from entering your ASP page, or avoiding NULLs in your data altogether,

see Article #2150 and Article #2073.


Why do I get 80040e4e errors?

284 requests - last updated Thursday, July 18, 2002

You may have come across this error, or something similar:

Microsoft OLE DB Provider for ODBC Drivers error '80040e4e'


Operation Canceled

Some possible solutions:

■ when connecting to Access, use a JET connection string, instead of the generic Access driver, if you have JET

installed - and whatever you do, avoid using a DSN (see Article #2125)

■ if you absolutely must use a DSN, try to avoid using an underscore in the name

■ do not pass username and password information to an Access connection string unless they are required,

and try adding DriverID=25; to the connection string (long shot)

■ use server.MapPath() if you are not sure of the absolute location of your MDB file

■ if you're using LDAP, see Q195387 and Q258845

■ if all else fails, make sure your server is up to date with all patches and security fixes (see Article #2151),

and update to the latest MDAC (http://www.microsoft.com/data/)


Why do I get 'Not enough space on temporary disk' errors?

277 requests - last updated Wednesday, July 10, 2002

You may have seen this error when working with Access:

Microsoft OLE DB Provider for ODBC Drivers error '8007000E'


[Microsoft][ODBC Microsoft Access Driver] Not enough space on temporary disk.
<file>.asp, line <?>

The error might also be system resource exceeded. This can be caused by at least two different things.

The first thing you should do is make sure that IUSR has sufficient permissions to the folder where JET*.tmp files

are created. This is typically in %windir%\temp\ or %systemdrive%\temp\ -- but you can check your exact location

by using SET at the command line, or checking the PATH properties in the Control Panel / System applet.

The second thing you should do is try to minimize the number of JET*.tmp files you need to create. For example,

when opening the database solely to retrieve data (no UPDATE, INSERT or DELETE), do so in adModeRead mode.

This way, Access will use the least amount of temporary files, since it doesn't need to prepare room or logic for

changing existing data. Here is an example of opening an Access database in adModeRead mode:

<%
cst = "<connection string>"
set conn = Server.CreateObject("ADODB.Connection")
conn.mode = 1 ' adModeRead
conn.open cst
%>
Why do I get script errors in Enterprise Manager's 'taskpad' view?

241 requests - last updated Thursday, May 2, 2002

Here's one we may have all seen, in SQL Server:

Internet Explorer Script Error


Line: 307
Char: 2
Error: Unspecified Error
Code: 0
URL:
res://D:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Binn\Resources\1033\sqlmmc.rll/Tabs.html

This is a known bug. I haven't seen it in a while, so I'm not sure if it has been fixed in Service Pack 2 or any of the post-

SP2 fixes that have been released. However, there is an easy workaround. Switch the database view to something else,

like Large Icons. Close Enterprise Manager. Open Enterprise Manager again, and switch the view back to Taskpad. This

should regenerate tabs.html and any dependant HTML files.


Why do I get 80040e30 errors?

239 requests - last updated Wednesday, July 17, 2002

When using a command object, or creating tables, you may be using named constants like adVarChar or adDate. If

you haven't defined these constants correctly (or forgot to include ADOVBS.inc), you might see this error:

Microsoft JET Database Engine error '80040e30'


The given type name was unrecognized.
or
Type name is invalid.
or
At least one parameter contained a type that was not supported.

Make sure your constants are defined correctly (see Article #2112 for better alternatives to including ADOVBS.inc)

and, further to that, make sure they're spelled correctly when you use them. When you can avoid using situations

which require the use of named constants (e.g. using a command object for executing a stored procedure), I

strongly recommend you do so (e.g. you could use a simple EXEC procedureName @param='whatever' SQL string

against a connection object instead).


Why do I get 800A0C93 errors?

170 requests - last updated Saturday, August 17, 2002

If you are getting one of these errors:

ADODB.Recordset error '800a0c93'


Operation is not allowed in this context.

or

ADODB.Recordset error '800a0c93'


The operation requested by the application is not allowed in this context.

or

ADODB.Recordset error '800a0c93'


Illegal Operation.

If you are using an ADODB.Recordset to UPDATE / INSERT (e.g. using the AddNew method), use a direct SQL

statement instead (see Article #2191). If using SQL Server, use a stored procedure (see Article #2201).

If you are using a DSN, try using an OLE-DB, DSN-less connection (see Article #2126).

If you are attempting to use MovePrevious on a forward-only / default recordset, consider the requirement again...

why would you need to MovePrevious? If the records are ordered 'backwards', change the query to reverse the

ORDER BY. If you absolutely must use MovePrevious, then consider using an AdOpenKeyset recordset.

If you are using ADO constants such as 'AdOpenDynamic', make sure you've included ADOVBS.inc (however read

Article #2112 first).

If you are trying to 'page' through a recordset, using AbsolutePage and PageSize properties, see an efficient

alternative in Article #2120.

If you are trying to binaryWrite image data to an ASP page that also has plain text content, consider retrieving this

data in a separate file and pointing to it via <IMG SRC>. You can also see Q276488 for ways to use ADODB.Stream
to return binary content from a database, without using the GetChunk method.
Why do I get 80040E24 errors?

145 requests - last updated Monday, August 19, 2002

Microsoft OLE DB Provider for SQL Server error '80040e24'


Rowset does not support fetching backward.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e24'


Rowset does not support fetching backward.

or

Microsoft JET Database Engine error '80040e24'


Rowset does not support fetching backward.

If you are attempting to use MovePrevious or MoveLast, consider changing your ORDER BY statement to reflect the

order in which you want to process records. All recordsets being written out to ASP should be processed forward-

only; if you absolutely need to move backwards, consider creating an array using GetRows(), and traverse the array

in reverse order. If you are only interested in the last record, reverse your ORDER BY and use SELECT TOP 1 in your

SELECT query.

If you are attempting to get the RecordCount, see Q246636 for a potential explanation, and Article #2193 for

alternative methods.

If you've set the cursorType of an ADODB.Recordset, you should re-consider whether you need a recordset object

with special properties (see Article #2191) and, if so, make sure you include the appropriate constants from

ADOVBS.inc (see Article #2112).

For further information, see Q306385 and Q306388.


Why do I get 80040E2F errors?

138 requests - last updated Sunday, August 18, 2002

Microsoft OLE DB Provider for ODBC Drivers error '80040e2f'


[Microsoft][ODBC Microsoft Access Driver]Error in row

This can happen if you are attempting to delete records while looping through an ADODB.Recordset object. Use

regular DELETE statements instead of ADODB.Recordset's DELETE method:

<%
' ...
conn.execute("DELETE table WHERE column ...")
%>

This will be more efficient code, and will alleviate the 'error in row' problem.

Microsoft OLE DB Provider for ODBC Drivers error '80040e2f'


[Microsoft][ODBC SQL Server Driver][SQL Server]Violation of PRIMARY KEY
constraint 'PK_<tablename>'. Cannot insert duplicate key in
object '<tablename>'.

or

Microsoft OLE DB Provider for ODBC Drivers error '80040e2f'


[Microsoft][ODBC Microsoft Access Driver] The changes you requested to the
table were not successful because they would create duplicate values in the
index, primary key, or relationship. Change the data in the field or fields
that contain duplicate data, remove the index, or redefine the index to
permit duplicate entries and try again.

This is pretty straight forward; you're attempting to insert/update a row with a value that already exists, on a

column (or set of columns) that is defined as a primary key. By definition, a primary key can only have one 'copy' of

each unique value. Either investigate the coding logic that is generating these primary keys for insert/update, or re-

examine whether this column should, in fact, be a primary key. If the column(s) should be a primary key, consider
validating prior to insert... if someone is trying to insert a new row that already exists (at least in terms of the

primary key), stop them BEFORE the error is tripped up.

Microsoft OLE DB Provider for ODBC Drivers error '80040e2f'


[Microsoft][ODBC Microsoft Access Driver] You cannot add or change a record
because a related record is required in table '<tablename>'.

Sounds like you have a foreign key defined, and are not satisfying its requirements. E.g. if you have table Parent,

with an Autonumber column, and table Child, with a foreign key that references table Parent's Autonumber column,

you should check that you are inserting into table Parent first, and that any updates to table Child are still

referencing a valid column in table Parent. (This should be done in the reverse order when deleting records... you

should delete from table Child first, then the 'master' record from table Parent.)
Why do I get 80040E54 errors?

111 requests - last updated Friday, September 20, 2002

Provider error '80040E54'


Number of rows with pending changes exceeded the limit.

This error can happen if you affect too many rows with an objRS.UpdateBatch call, or an ADODB.Recordset opened

with adLockBatchOptimistic. Please, do yourself a favor. If you want to update rows in a table, use this

methodology, instead of an efficient loop:

conn.execute("UPDATE table SET column='value' WHERE [...]")

Using a recordset, which is meant for *retrieving* data, to enforce updates on a table is wasteful, and leads to

errors such as this. For more information on avoiding a recordset for non-data-retrieving operations, see Article

#2191.

If you absolutely must use an ADODB.Recordset for updating rows, I encourage you to read Article #2191 again. If

you *still* find that you need to do this (e.g. pointy-haired boss syndrome), make sure you break it into chunks, use

a static or keyset cursor, and be sure that you always close your recordset objects / set them to nothing.
Why do I get 80070070 errors?

109 requests - last updated Sunday, September 15, 2002

You may have come across this error:

<object> error '80070070'


There is not enough space on the disk.

This is exactly what it sounds like... one of the disks that your ASP page is (perhaps indirectly) trying to write to is

full. It may be the drive the ASP page is running in, or creating a file in (which could be a share on another

machine). It may be the drive containing the temp folder(s) or the page file(s). If you are using CDONTS, it could be

the SMTP server.

Check the code, and analyze where it might be writing files; then, check those locations for sufficient space.
Why do I get 8002000A errors?

103 requests - last updated Sunday, September 15, 2002

Provider error '8002000a'


Out of present range.

This is usually caused when you pass a numeric value too large for the data type, when using a ADODB.Command.

For example, sending 2^32 to a parameter using adInteger. A decent way to avoid this strong typing issue

altogether is to avoid the command object and use connectionObject.Execute("EXEC procName"). See Article #2201

for more information on executing stored procedures from ASP without using a command object.
How do I return row numbers with my query?

60 requests - last updated Monday, December 9, 2002

Often, people want to "invent" an identity, or rank, on the fly. So their original result set would look like this:

Lastname Firstname
-------- ---------
Evans Bob
Smith Frank

And they would want this:

Rownum Lastname Firstname


------ -------- ---------
1 Evans Bob
2 Smith Frank

This would act like Oracle's ROWNUM, which isn't supported in SQL Server.

Of course, once you've retrieved this resultset into your ASP page, you could use a counter to increment as you're

processing.

Another way to mimic this is to use an intermediate temp table, e.g.:


SELECT
IDENTITY(INT,1,1) AS RowID,
*
INTO
#table
FROM
table
-- ORDER BY
-- ??

SELECT
*
FROM
#table
ORDER BY
RowID

Also, be sure to read Q186133.


How do I get a list of tables and their record counts?

60 requests - last updated Tuesday, December 10, 2002

SQL Server

This is undocumented, so please don't rely on it, or use it in production code. The sp used may change or be

disabled in a future release or service pack / hotfix, or it could disappear altogether. So this tip is mainly for creating

your own diagnostic page to give you a quick overview of how many records are in each table in a specific database.

Create a stored procedure that looks like this:

CREATE PROCEDURE listTableRecordCounts


AS
BEGIN
SET NOCOUNT ON

CREATE TABLE #foo


(
tablename VARCHAR(255),
rc INT
)

INSERT #foo
EXEC sp_msForEachTable
'SELECT PARSENAME(''?'', 1),
COUNT(*) FROM ?'

SELECT tablename, rc
FROM #foo
ORDER BY rc DESC

DROP TABLE #foo


END

Then call it from ASP as follows:


<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection_string>"
set rs = conn.execute("EXEC listTableRecordCounts")
if not rs.eof then

response.write "<table><tr>" & _


"<th>Table name</th>" & _
"<th>Records</th></tr>"

do while not rs.eof


response.write "<tr>" & _
" <td>" & rs(0) & "</td>" & _
" <td>" & rs(1) & "</td>" & _
"</tr>"
rs.movenext
loop

response.write "</table>"
end if
rs.close: set rs = nothing
conn.close: set conn = nothing
%>

Note that this will only count USER tables, not system tables. You could consider creating this procedure in the

master database and marking it as a system object; this way, you could execute it within the context of any

database, instead of having to create a copy of the proc for each database.

Access

Coming soon...
How do I store objects or components in session/application variables?

22,534 requests - last updated Sunday, September 15, 2002

Please don't.

Many people seem tempted to store objects in the session, so that they don't have to create instances on each page.

This is particularly true for connection objects and recordsets.

They assume this is more efficient, but it is not. This is the absolute worst use of resources you can have

(particularly with ADO objects) and will significantly reduce your application's scalability. See these articles for more

details:

Q258939 Recommendations for Connecting to Databases Through IIS

http://www.microsoft.com/mind/1198/ado/ado.htm (tip #6)

Q176056 INFO: ADO/ASP Scalability FAQ

Here are some other KB articles that should help clarify the issues at hand (I hope you didn't expect this to be light

reading):

Q243544 INFO: Component Threading Model Summary Under Active Server Page

Q191979 PRB: VB Component Not Marked Apartment Produces ASP 0115 Error

Q243548 Design Guidelines for VB Components Under ASP

Q243543 Single-Threaded Apartment Objects in Session or Application

Another possible outcome of storing an improperly threaded object in session or application scope is the following:

<object> error '8001010E'


The application called an interface that was marshalled for a different thread.
Make sure that your object is explicitly written for this scope before you attempt to utilize it this way. (Hint: no VB6

object is threaded correctly for use in session / application scope.)

Note that it is possible to safely store a recordset in the session, by first converting it to its array form using

GetRows().

There is at least one exception to this 'rule' - it is acceptable to store the free-threaded version of the MSXML DOM

Document object in application scope, for example:

<%
Set DOM = Server.CreateObject("MSXML2.FreeThreadedDOMDocument")
DOM.loadXML "<somexml><somenode/></somexml>"
Set Application("DOM") = DOM
%>
DLL: How do I avoid 'Permission Denied' when re-compiling?

12,560 requests - last updated Sunday, October 28, 2001

Use this batch file to release IIS' lock on your DLL:

net stop iisadmin /y


net start w3svc

You may need to add start lines for other services such as smtpsvc or msftpsvc. The disclaimer of course, if you're

worried about stopping your web site traffic: don't develop DLLs on production machines.

You may also find the need to use KILL.EXE, if iisadmin refuses to be shut down. KILL.EXE is found in the NT and

Windows 2000 Resource Kits. As long as the .EXE is in your system's path, you can modify the above script as

follows:

net stop iisadmin /y


kill iisadmin
net start w3svc

In Windows 2000, you can use a much faster process by issuing the following command (again, at a command

prompt or in a batch file):

iisreset

You can also issue stop and start commands independently:

iisreset /stop

iisreset /start

If your object is hosted in MTS (or an application running in its own memory space), you should be able to just

unload that application / package. Similarly, for objects hosted in COM+, shutting down the application from
Component Services should unlock any holds IIS has on your DLL.

If you are developing your ASP files using Visual InterDev, then IntelliSense may be doing you a disservice. Since

this feature actually hooks into your custom COM objects (once you've created them using their ProgID in a

createobject() statement), this places a lock on the DLL similar to the one IIS places on it. So if you are editing an

ASP file which calls the DLL in question, you can recompile without rebooting by simply closing the ASP file.
When does ASP release COM objects?

9,178 requests - last updated Sunday, August 13, 2000

Page-level

If you have page-level objects and are running IIS 4.0, they are not released when you issue the following

command:

<%
set object = nothing
%>

Page-level objects are NOT released until the page goes out of scope (and, by some reports, are not released at all if

the object is not explicitly destroyed, as per above).

IIS 5.0 cleans this up quite a bit; since it releases the object THE INSTANT you set it to nothing, there is suddenly a

great advantage to releasing it explicitly as early as possible.

Session-level

Session-level objects, unless you explicitly release them from the session yourself, are (allegedly) destroyed when

the session ends. This depends on how the session ends, of course. See Article #2078 for more information.

There are few scenarios where you should be using objects in session variables (see Article #2053). In about 99% of

all cases, objects should be created and destroyed at the page level.
How do I detect browsers without components?

8,414 requests - last updated Wednesday, December 4, 2002

We all know that to make a successful and attractive web site, we must strictly adhere to HTML 3.2 standards,

avoiding all temptations to use any browser- or platform-specific tags and features.

Yeah, right. And 640k of RAM ought to be enough for anybody.

For the rest of us, we have to find innovative ways to present browser-specific content, yet still have it degrade

gracefully for everyone else. This can be relatively straightforward for individual elements, such as ActiveX controls,

cascading style sheets and even Java applets. But what if you want to present a certain paragraph only to IE4

users? or only let Netscape users see the contents of a specific <div> element? or make your body text 10pt for a

Mac, and 9pt for a PC?

There is a way to do these things, without using COM objects, a database, or 200 tedious client-side document.write

commands. All you need is a little familiarity with the user agent string of the browser(s) you wish to target.

Let's start with getting at that string in the first place. It is available in the server variable "http_user_agent" and

can be accessed as follows (I convert this variable to lower case, in order to prevent ambiguity):

<%
myUA = Request.ServerVariables("HTTP_USER_AGENT")
ua = lcase(myUA)
Response.Write uA
%>

This code will produce a string similar to one of the following:

mozilla/4.0 (compatible; msie 5.5; windows nt 5.0)


mozilla/4.5 [en] (win98; i)

Be aware that there are literally thousands of unique user agent strings. Thankfully, the ones you will likely be most

concerned with share common characteristics. Also, depending on the purpose of your discrimination, most can be
grouped into similar categories.

Let's take DHTML for example. Say you want to have a headline that, when clicked on, presents an "abstract" and a

link to more information (instead of linking the user right away). The code would look something like this:

<a href="#top" onClick="myDiv.style.display='';return false;">This is a link</a>


<div style="display:none" id="myDiv"><br>This is an in-depth description of the link,
as well as <a href="#top">an actual link</a>.</div>

In IE3 and NN3, the description and link will always be visible, and in Netscape 4 it will never be visible. In most

cases the code above will generate a scripting error as well, which is not pretty. However, in IE4 or greater, the

description will be hidden until the user clicks on the headline. Wouldn't it be nice if you had the ability to regulate

that code, and ONLY insert the extra DHTML snippets for IE4 and IE5, preventing errors and other weird things in

Netscape?

Here's what you can do, based on the code above. Since all IE4+ user agent strings (including AOL and NeoPlanet

versions) are remarkably similar, you can search for "msie 4" or "msie 5" in the above strings. If this string is found,

you know that your visitor is using IE4 or better, and so it is safe to include your DHTML code. For example:

<%
myUA = Request.ServerVariables("HTTP_USER_AGENT")
ua = lcase(myUA)
ie4 = instr(ua,"msie 4")
ie5 = instr(ua,"msie 5")
ie6 = instr(ua,"msie 6")
ie7 = instr(ua,"msie 7")
' now that Netscape 7 is out, you know it's coming!

if ie4>0 or ie5>0 or ie6>0 or ie7>0 then


' You can include DHTML code:
%>

<a href="#" onClick="myDiv.style.display='';return false;">This is a link</a>


<div style="display:none" id="myDiv"><br>This is an in-depth description of the link,
as well as <a href="#top">an actual link</a>.</div>

<% else ' Don't include DHTML code: %>


<a href="#top">This is a link</a><br>This is an in-depth description of the link.

<% end if %>

If you find that certain script elements you use only work on certain platforms (e.g. Win32), you can add extra

search elements into your discriminatory code, searching for key elements like "x11", "mac", "win95", "win98", or

"winnt" -- the possibilities for this kind of browser detection are really only limited by your imagination and

creativity. I use it almost religiously when writing any ASP application... from scripting functions, to adding CSS

support, to deciding whether or not to tell people they're using an outdated browser.

If manual browser detection isn't for you, I strongly recommend you take a look at CyScape's BrowserHawk before

you resign yourself to using Browscap. BrowserHawk is a very powerful server-side component which makes

browser detection a breeze and, unlike Browscap, maintains itself as new browser versions are released. If you are

using Browscap, however, make sure you keep it up to date as new browsers are released (see Article #2199).
How do I generate PDF files from ASP?

6,896 requests - last updated Friday, October 18, 2002

There are several components out that there that will help you generate PDF files dynamically - whether based on

simple ASP variables, database queries or user input.

Acrobat FDF Toolkit (and a good companion article at 15 Seconds)

ActivePDF

ABCPdf

Appligent

ASPEasyPDF

Dreamscape PDFReport

DynamicPDF

iSEDQuickPDF

PDFLib

RPT Software

You may also want to snoop around this resource site:

http://www.pdfzone.com/
How do I determine if a COM object is installed?

6,033 requests - last updated Tuesday, May 8, 2001

As long as you know the ProgID (e.g. "Scripting.FileSystemObject"), you can easily determine if a COM object is

installed and available for you to use. In the following example, I am going to send an e-mail *if* ASPMail

("SMTPsvg.Mailer") is installed and registered:

<%
On error resume next
Set Mailer = Server.CreateObject("SMTPsvg.Mailer")
if err.number <> 0 then
Response.Write "ASPMail is not installed."
else
Mailer.RemoteHost = "<some remote SMTP host that allows relay>"
Mailer.FromName = "Me!"
Mailer.FromAddress = "me@me.com"
Mailer.Subject = "Test"
Mailer.BodyText = "Hello"
Mailer.Addrecipient "you@me.com","you@me.com"
if not Mailer.sendMail then Response.Write mailer.response
set Mailer=nothing
end if
%>

If you're not making code decisions based on the results of the check, you can just print out a result. For example I

have a status ASP page I check on all servers we add to our farm, to make sure all COM objects are installed,

registered, licensed where applicable, and work as coded. Here is the type of code I use:
<%
On error resume next
if isObject(Server.Createobject("SMTPsvg.Mailer")) then
response.write "ASPMail is installed."
else
response.write "ASPMail is not installed."
end if
%>

Of course, knowing the object is there is only half the battle sometimes; CDONTS is particularly gnarly to get

working properly, and FileSystemObject can be extremely permissions-sensitive.


Why does Browscap give me 'unknown' or tell me IE is Netscape?

4,689 requests - last updated Wednesday, December 4, 2002

Your Browscap.ini file is probably outdated, or you are using an obscure browser. Unfortunately, developers are

pretty much left on their own these days when it comes to Browscap.ini. See Article #2199 for information on

obtaining a more recent copy of Browscap.ini.

If you want to avoid using Browscap, see some tips for manually detecting browsers in Article #2184.

In addition, CyScape makes a great browser detection component called BrowserHawk -- reducing the need for

much of the manual coding to provide proper browser detection. You can read about its advantages over the

Browser Capabilities component at http://cyscape.com/products/bhawk/bcadv.asp.


Why do I get 'Server.CreateObject Access Error'?

4,185 requests - last updated Monday, January 22, 2001

If this is happening with your own DLL, or a third-party component, make sure IUSR_<machine_name> has access

rights to the DLL. See Q198432 for more info. With IIS 5.0, you might also want to make sure that the application

which is using the object is set to LOW under 'Application Protection' in Internet Services Manager.
Why do I get 8007000E errors?

3,538 requests - last updated Friday, November 8, 2002

This error can be caused by various things. Errors you might see are "System Resource Exceeded" and "Not Enough

Storage Is Available to Complete This Operation." You should see the following KB articles to see if any of these

situations apply to your problem:

Q248668 - BUG: "Not Enough Storage Is Available to Complete This Operation" with Oracle OLE DB

Q189782 - BUG: RegQueryValueEx() Fails When lpValueName Is NULL

Q182423 - FIX: "Not Enough Storage is Available..." Error with ADO

Q174776 - Index Server Queries Return Not Enough Storage Is Available Error

Q254759 - BUG: ListAvailableServers Method of the SQLDMO.Application Object Causes Error 0x800A000E

Otherwise, a sure way to alleviate the problem is to upgrade or reinstall the latest version of MDAC

(http://www.microsoft.com/data/).
Why do I get 800401F3 / 800A01AD errors?

3,383 requests - last updated Saturday, September 14, 2002

This error is usually associated with trying to create an instance of a COM object using a ProgID that is not actually

registered on the machine. The error message might be one of the following:

Server object error 'ASP 0177 : 800401f3'


Server.CreateObject Failed
/<file>.asp, line <line>
Invalid class string

Server object error 'ASP 0177 : 800a01f3'


Server.CreateObject Failed
/<file>.asp, line <line>
Invalid class string

Microsoft VBScript runtime (0x800A01AD)


ActiveX component can't create object: <progid>
/<file>.asp, line <line>

Common causes for this are:

1. forgetting to register the object using regsvr32

2. fat-fingering the ProgID (e.g. "CDNOTS.NewMial")

3. not realizing that the object is not actually installed

4. using an MSWC or IIS sample component that is not installed (see Q249290 for more info)

See Article #2135 for information on determining if a COM object is installed on your server.

If you are getting the following error:


Server object error 'ASP 0177 : 80080005'
Server.CreateObject Failed
/<file>.asp, line <line>
Server execution failed

See Q241057 for information on setting your MTS package identity appropriately.
Where can I get a shopping cart for my web site?

2,779 requests - last updated Thursday, April 11, 2002

If you want to roll your own cart, can't deploy custom COM objects on your web site, or must not support cookies /

session variables, you can look at the very simplistic sample code at http://www.aspfaq.com/cart/ to get you started

... nothing beats the education (and flexibility!) you gain from writing your own application. However if you lack the

time or ability, here is a list of free and commercial components, as well as some pure ASP solutions. I have used

ActiveCart and it is fairly well written; I have also heard very good things about IISCart.

a.shopKart

A+ Store E-Commerce

A-Cart

AceFlex B2C

ActiveCart 3.0

Affilius Shopping Cart

ASP 101 Sample Code

ASPBasket

ASPCart

ASP Shop Plus

ASP Simple Store

BridgeCart

BugMall Shopping Cart


CactuShop Shopping Cart

Cart32

CartEasy

Charon Cart 2.0.2

ClickCart!

Comersus Cart

CyberShop

CyberStrong eShop

DevInteractive's Web-Cart

Envision eStore

iHTML Merchant

IISCart

Line9 Lite

Line9 Pro

MetaCart Free

MetaCart2 (PayPal)

opuslabs' ShopShop.Cart

QuadComm Q-Shop
SalesCart

Sams' ASP in 24 Hours Sample Code

StoreFront 5.0

Ultra Cart II

Virtual Brochure Cupboard

VP-ASP Cart

WebStores Developer

Xtensible Shopping Bag

ZmeY Shopping Cart


Where can I get an updated Browscap.ini?

1,813 requests - last updated Wednesday, December 4, 2002

Many people view MSWC.BrowserType as the holy grail, taking care of all of their browser detection issues. I'm not

going to lie to you: Browscap.ini is a PITA. You have to update this file with umpteen new entries every time a new

browser version is released.

There used to be several places where you could download a new version, which someone else updated, pretty much

within days of a new User Agent hitting the logs. Microsoft denied responsibility for this file almost immediately after

it was born (the version that ships with XP has User Agent strings containing "Windows 2000" and its highest

Navigator version is 4.0 Beta 2!); Cyscape has not updated theirs since February of 2000; and asptracker.com seems

to have been bought out by the pornlords.

Understandable. Given the explosion of browser versions, and the public availability of betas of betas of betas, it's

simply too time-consuming to sit there updating a file with new versions, and making sure your new entries don't

step on the feet of any already in the file.

You can use Juan Llibre's version, or GaryKeith.com's version (note that this will only work with IIS 5.0, 5.1 and 6.0 -

4.0 is not supported). And you can download both, making your own adjustments as you see fit.
How do I pass server-side values to a client-side ActiveX control?

1,616 requests - last updated Tuesday, October 30, 2001

Many people go way overboard and try to dream up elaborate ways to have a client-side ActiveX control

communicating with the server to retrieve values. As long as these values are not dependent on user activities

AFTER the ActiveX control has been loaded, try something like this:

<%
strValue = "some string"
intValue = 5
Response.Write("<OBJECT clsid=[blah blah]>")
Response.Write("<PARAM NAME=INT VALUE=""<%=intValue%>"">")
Response.Write("<PARAM NAME=INT VALUE=""<%=intValue%>"">")
Response.Write("</OBJECT>")
%>

ANd now in server-side JavaScript:

<script language=JavaScript runat=server>

var strValue = "some string";


var intValue = 5;
Response.Write("<OBJECT clsid=[blah blah]>");
Response.Write("<PARAM NAME=INT VALUE=\"<%=intValue%>\">");
Response.Write("<PARAM NAME=INT VALUE=\"<%=intValue%>\">");
Response.Write("</OBJECT>");

</script>
Can I code ISAPI filters / extensions with Visual Basic?

1,409 requests - last updated Thursday, July 25, 2002

Not directly, because ISAPI filters and extensions require functionality only found in C++ (or Delphi). For more

information, see Developing ISAPI Extensions and Filters at MSDN Online. The reason most people ask about this is

that they want to know how to access http://theirsite/theirDLL.dll?whatever much like expedia does (e.g.

http://www.expedia.com/pub/agent.dll?qscr=cmfd&itid=&itdx=&itty=&ecid=&subm=0).

However, there are some links out there that allegedly help make this possible (though the overhead of hitting the

VB runtime with every request to your web server is something you definitely want to weigh in your testing). You

can see some of Microsoft's benchmarks here.

VB Bridge

SpyWorks

OLEISAPI (outdated)

I have not tried any of these methods, so do not vouch for their validity.
Should I instantiate my COM object with CreateObject or Server.CreateObject?

946 requests - last updated Wednesday, July 24, 2002

Server.CreateObject marshals the ASP intrinsic objects (e.g. Request, Session). When you use CreateObject, the

component does not have direct access to these objects. So if your component needs to access, say, the Response

object, you should use Server.CreateObject. In addition, Server.CreateObject correctly calls any (now obsolete)

onStartPage and onEndPage methods at the appropriate times, whereas CreateObject cannot be relied upon for

this.

CreateObject instantiates the object in the context of the scripting engine, as opposed to that of the ASP page itself.

This is fine in a client-server or single user application, but in a web page environment, the fact that CreateObject

bypasses automatic memory management makes it a poor and less scalable choice.

Server.CreateObject allows the component to participate in the same MTS/COM+ transaction as the ASP page itself,

and also provides process isolation. If you use CreateObject, the component can still take part in a transaction, but

the ASP page can not control nor determine the commit/rollback of the component's activities, since they are in

separate transactions.

CreateObject, in the case of IIS 4.0, is much better at cleaning up after itself. It gets garbage collected before the

page is finished, whereas Server.CreateObject waits until the page goes out of scope. This is no longer the case in

IIS 5.0 and above, which is much smarter about how it cleans up objects - as soon as you destroy them.

You cannot use Server.CreateObject when accessing a COM object through a firewall (e.g. in a distributed system).

See Q193230 for more information.

So, the only time you would want to use CreateObject is if you had a very small user base, against IIS 4.0, with a

component that is very resource-intensive.


How do I generate RTF documents from ASP?

450 requests - last updated Tuesday, August 27, 2002

Here are several components designed to convert HTML to RTF, and/or vice-versa.

Doc2HTML

MyLittleWriter

Net2RTF

RTF2HTML

RTFConverter

TX Text Control

WordToy

If you want to do this yourself, there are several articles out there that can help.

This google message has a pretty thorough technical description of the RTF document itself.

And once you have an RTF format (e.g. a template) set up, Q270906 will get you up and running from ASP.

There are a couple of other articles that might be useful:

15 Seconds

Planet Source Code

And the following code samples are written in VB, but could be converted to ASP with little difficulty:

Developer Fusion
Planet Source Code
Why do I get 8000401A errors?

413 requests - last updated Sunday, September 15, 2002

When running a component in an MTS or COM+ application, you may have come across one of the following errors:

ASP 0177 : 8000401a


Server.CreateObject Failed
/<file>.asp, line <line>
The server process could not be started because the configured identity is incorrect. Check the username and password.

or

ASP 0178 : 80070005


Server.CreateObject Access Error
/<file>.asp, line <line>
The call to Server.CreateObject failed while checking permissions. Access is denied to this object.

The problem is that the 'Identity' set up for the application lacks sufficient privileges. This could be for several reasons, but before we get into

those, let's check out where this identity is configured. Open Control Panel, Administrative Tools, and double-click Component Services. Expand

COM+ Applications, right-click the application in question, hit Properties, and switch to the Identity tab. You should see a dialog similar to the

following:
One possible reason is that, when setting the identity for your application, you simply chose a user that doesn't have access to the DLL, or to a

file or system call the DLL touches.

Another potential cause is that someone changed the Windows password for that user (since the application won't automatically adjust for that

change), or it was never entered correctly in the first place.

If this identity is set to the 'Interactive User', then the above error might occur when the user currently logged in to the machine is a peon, or

when nobody is logged in at all. This is the primary reason why this error is never detected in development... the user developing the COM+

application, and in complete control of the machine, is also the user logged in and running as the interactive user.

You should make sure to use a local user account with sufficient privileges, if that's what your DLL requires.

See Q276407 for more information.


How do I determine if an object exists?

284 requests - last updated Thursday, August 29, 2002

<%
set conn = server.createobject("ADODB.Connection")

response.write isObject(conn)
response.write isNull(conn)
response.write isEmpty(conn) & "<p>"

set conn = nothing

response.write isObject(conn)
response.write isNull(conn)
response.write isEmpty(conn)
%>

When the object has been created, isObject() returns true, while isNull() and isEmpty() both return false. When the

object has been destroyed, isObject() still returns true, and isNull() and isEmpty() both still return false. This

information isn't very useful, as it doesn't change during and after the lifetime of an object.

The criteria you are looking for is whether the object "is nothing", as demonstrated here:

<%
set conn = server.createobject("ADODB.Connection")

response.write (conn IS NOTHING) & "<p>"

set conn = nothing

response.write (conn IS NOTHING) & "<p>"

if NOT (conn is nothing) then


response.write "Destroying conn."
set conn = nothing
else
response.write "conn doesn't exist."
end if
%>

Note that some components, e.g. ADODB.Recordset, have properties that will tell you their state. For example, see

the last code snippet in Article #2283.


Why do I get 80040111 errors?

255 requests - last updated Sunday, August 25, 2002

Microsoft VBScript runtime error '80040111'


ClassFactory cannot supply requested class

or

Server Object error 'ASP 0177 : 80040111'


Server.CreateObject Failed
/<file>.asp, line <line>
ClassFactory cannot supply requested class

You are probably attempting to instantiate a COM object that is not installed - or is installed more than once. If this

is your own COM object, you might have to scrounge through the registry and delete instances of ProgIDs that point

to older version(s) of the object. Another option is to re-compile the COM object with a new ProgID. If you are not

using MTS/COM+, make sure binary compatibility is enabled... this way re-registering the component will replace

the old registry references, instead of adding a new GUID each time you compile.

If you are trying to use MSXML's ServerXMLHTTP class, you cannot use this object in Windows 95 or Windows 98, or

on an NT 4.0 machine with an IE version 5.0 or earlier (see Q279129).

If you are attempting to create a stock ProgID, e.g. Scripting.FileSystemObject, you might have a corrupted script

engine - try reinstalling from msdn.microsoft.com/scripting/. If the ProgID is ADODB.Connection, or

ADODB.Command, or ADODB.Recordset, or ADODB.Parameter, then you probably have a corrupt MDAC install -

apply the latest version (http://www.microsoft.com/data/).

If none of the above suggestions help, you might try downloading FileMon and RegMon from www.sysinternals.com;

these great utilities can help you pinpoint problems in file or registry access points.

Exchange

If you are seeing this error code in the Event Log, associated with Event ID 11, then you may have an Exchange /

OWA issue; see Q222066.


If you are getting an error with Outlook Web Access (or a custom application which accesses Exchange through CDO

/ ASP):

MAPI 1.0 - [MAPI_E_LOGON_FAILED(80040111)]]

Make sure you are not using anonymous access, and that users are authenticated with at least Basic Authentication

(Windows / IE clients should use Windows Authentication). See Q181739 for more information, and other possible

options. For some great information on enabling CDO within ASP, see http://www.cdolive.com/asp2.htm.
How do I handle MD5 from ASP?

252 requests - last updated Wednesday, September 4, 2002

You can include this JavaScript code in your ASP page, by using <script language=javascript runat=server> around

it, and then calling its methods from VBScript:

http://pajhome.org.uk/crypt/md5/md5src.html

There are also some components out there:

ANEI-MD5

AspEncrypt

C2Go's C2GMD5 1.0

DigiSign 2.0

Frez MD5 digest(free)

MD5DLL(free)

MD5 Toolkit

Nelson Gomes' MD5 hash for IIS

NewObjects' Hash ActiveX Objects

And if you are looking for an extended stored procedure for SQL Server 7.0 or 2000:

XP_Crypt
Why do I get 8007007E errors?

239 requests - last updated Saturday, September 14, 2002

When using Server.CreateObject, you may have come across this error:

Server object, ASP 0177 (0x8007007E)


8007007e
<file>.asp, line <line>

or

Provider Error '8007007E'


The specified module could nto be found.

Make sure that the DLL you are trying to instantiate has actually been registered on the system. A DLL you created

in VB will work within the VB environment, but will not work from other interfaces (such as ASP) until you register

the code as follows (from the command line):

regsvr32 <path>\<file>.dll

Next, check IUSR permissions on the DLL, and any folders, files or executables it might be accessing.

If you are attempting to instantiate FileSystemObject, make sure that no anti-virus or other programs are blocking

access to script components, and try re-register scrrun.dll, with the following code at the command line:

regsvr32 %windir%\system32\scrrun.dll

If you are trying to use CDONTS.NewMail, use CDO.Message instead, especially if you are running Windows XP. See

Article #2026 for more information.


Why do I get 80040112 / 8007045A errors?

233 requests - last updated Wednesday, October 2, 2002

Server object error 'ASP 0177 : 80040112'


Server.CreateObject Failed

or

Server object error 'ASP 0177 : 8007045a'


Server.CreateObject Failed

This can happen when you try to use Server.CreateObject("InetCtls.Inet.1"). This object is really not recommended

for server usage; you should use XMLHTTP instead (see Article #2173).

006~ASP 0177~Server.CreateObject Failed~80040112

This can happen when you try and use DAO to compact / repair an Access database. See Article #2190 for code that

uses JRO instead of DAO.

To see other potential solutions for failed CreateObject situations, see Article #2134, Article #2323, Article #2316,

Article #2323, Article #2360, Article #2379, and Article #2393.


Why do I get 800A005B errors?

194 requests - last updated Monday, August 19, 2002

<object> error '800a005b'


Object variable or With block variable not set

or

Microsoft VBScript runtime error '800a005b'


Object variable not set

Make sure to use the SET keyword when creating objects (or calling methods that return objects), and that you use

Server.CreateObject as opposed to CreateObject, if you are expecting to use ObjectContext. According to Q243772,

you should also register your component in MTS as opposed to running it standalone, though I've never been forced

to do that (it's always been a different problem).

If you have code such as the following:

Dim objConn As ADODB.Connection


objConn.open "<connectionString>"

Make sure you actually instantiate an object using the NEW keyword, e.g.:

Dim objConn As ADODB.Connection


Set objConn = New ADODB.Connection
objConn.open "<connectionString>"

If you have complex looping or if/else structures in your component, make sure your nesting is complete and you

don't have any dangling structs that weren't picked up by the compiler.

Do not attempt to use ObjectContext functionality within Class_Initialize() / Class_Terminate (see Q250309).
Why do I get 80072EE5 errors?

162 requests - last updated Sunday, August 25, 2002

Microsoft VBScript runtime error '80072ee5'


The URL is invalid.

First off, make sure your URL is valid ... e.g. begins with http:// or https:// and has a properly encoded querystring.

While version 4.0 is recommended, if you are using the 3.0 version of MSXML's ServerXMLHTTP class, and open a

URL with more than 2083 characters, you will get this error. If you upgrade to MSXML 3.0 SP1, you will get the error

0x80004005 (Unspecified error). See Q291088 for more information - though this article casually suggests

reproducing the error with 3000 characters... which doesn't really tell you as much as you might want to know.

Another way this can happen is if you pass a URL that contains more than two space characters, which have not

been URL encoded. So, in addition to making sure your URL is shorter than 2083 characters, you should be careful

to use Server.URLEncode on all querystring parameters to properly encode trouble characters, and avoid this error.

For more information, see Q312989.


Why do I get 80040460 errors?

155 requests - last updated Wednesday, August 7, 2002

The only object I know of that will produce this error is ASPSmartUpload. The error is as follows:

aspSmartUpload.File error '80040460'

Unable to save file (Error 1120)

/<file>.asp, line <line>

This is usually due to a permissions problem. Make sure IUSR_<machineName> (or the authenticated user) has

write permissions to the folder indicated in the save method, and that this folder is referenced in absolute local path

terms (e.g. c:\folder\).


Why do I get 80072EE2 errors?

152 requests - last updated Sunday, September 15, 2002

msxml3.dll error '80072ee2'


The operation timed out

This means that the site you were trying to parse either could not be found, and the component gave up; or it is

taking far too long for the page to finish loading. One way you can avoid this error is to set timeout values that are

more conservative, e.g.:

<%
url = "http://www.espn.com/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")

' resolve, connect, send, receive - in milliseconds


xmlhttp.setTimeouts 5000, 60000, 10000, 10000

xmlhttp.open "GET", url, false


xmlhttp.send ""
Response.write xmlhttp.responseText
set xmlhttp = nothing
%>

The four timeout values are as follows:

resolveTimeout

This value is for the return of a DNS resolution (mapping the domain name to its representative IP address). The

default value is infinite.

connectTimeout

This value is for establishing a connection with the server, and the default value is 60 seconds.

Long integer. connectTimeout is applied to establishing a communication socket with the target server, with a

default timeout value of 60 seconds.


sendTimeout

This value is the time allowed for sending an individual packet of data to the server. The default value is five

minutes.

receiveTimeout

This value is the time allowed for receiving an individual packet of data from the server. The default value is 60

minutes.
Why do I get 800A0030 errors?

145 requests - last updated Wednesday, August 14, 2002

You may have come across this error:

Microsoft VBScript runtime error '800a0030'


Error in loading DLL

If you are creating or using a Visual Basic 5.0 ActiveX DLL that returns a collection, see Q178777. If you are creating

or using a Visual basic 5.0 or 6.0 ActiveX DLL that returns a user-defined type (UDT), see Q224397.

If you get this error when instantiating an ADODB.Connection or other built-in component, re-install the latest

refresh of MDAC (from http://www.microsoft.com/data/).


Why do I get 80040514 errors?

109 requests - last updated Sunday, September 15, 2002

When trying to automate Word.Application, Excel.Application or even MAPI.Session from the server, you have come

across this error:

Server object error 'ASP 0196 : 80040154'


Cannot launch out of process component
/<file>.asp, line <line>
Only InProc server components should be used. If you want to use LocalServer
components, you must set the AspAllowOutOfProcComponents metabase setting.
Please consult the help file for important considerations.

Like the error states, one way to allow this is to set AspAllowOutOfProcComponents setting to 1, using MetaEdit.exe

(see Article #2227 for instructions on setting a similar value in the metabase editor). You can also see several other

approaches to changing metabase settings in Q184682.

Before changing the metabase, however, try setting IIS to allow scripts and executables, and play with the process

isolation of the site. These settings may be preferable to changing the metabase...
Why do I get 800C0007 errors?

99 requests - last updated Sunday, September 15, 2002

It is possible that this error is due to a fauly script engine install (reinstall from this crazy URL). More likely,

however, you are using the MSXML2.DOMDocument object to retrieve XML data from a URL, and receiving the

following error:

msxml3.dll error '800C0007'


No data is available for the requested resource.

If this is the case, you should use MSXML2.ServerXMLHTTP to retrieve the data, then use the DOMDocument object

to parse it. See Article #2173 for plenty of samples (and see Q281142 for more information about the error, and a

different workaround).
How do I upload files from the client to the server?

27,335 requests - last updated Wednesday, April 3, 2002

For your users to upload files, you must provide them with the following interface:

<form method=POST name=form1 action=handler.asp enctype='multipart/form-data'>


<input type=file name=filename>
<input type=submit value=' Go >> '>
</form>

Keep two things in mind: (1) users of IE 3.02 or lower will need a Microsoft add-on to facilitate file uploads, and (2)

the enctype of the form precludes you from accessing non-file form elements directly (but most components deal

with this).

Now, to handle the uploaded file(s) from ASP, you typically need a component:

ASPUpload

ASPSmartUpload

ABCUpload

ASP Simple Upload

HugeASP Upload

Infomentum ActiveFile

MiniUpload

MS Posting Acceptor

SAFileUp
W3 Upload

Win ASP Up/Download Component

There are also a few ways to upload files without traditional 3rd party components:

Active Server Developer's Journal

ASP 101

aspfaqs.com

ASPFree

ASPZone

Curt C

PureASP

StarDeveloper

For exact syntax to handle the incoming file(s), please see the documentation and sample code that will be available

with whatever choice you make.


How do I change the target frame or window of a response.redirect?

23,547 requests - last updated Tuesday, July 16, 2002

With client-side code, such as top.<target>.location.href= ...

ASP is on the server and does not "see" framesets.

Instead of response.redirect, use code like this:

<%
url = "http://wherever.com/"
response.write("<script>" & vbCrLf)
response.write("parent.framename.location.replace('" & url & "');")
response.write(vbCrLf & "</script>")
%>

Or this:

<%
url = "http://wherever.com/"
response.write("<script>" & vbCrLf)
response.write("parent.framename.location.href='" & url & "';")
response.write(vbCrLf & "</script>")
%>

I prefer the replace() function because it doesn't muck up the history list.

If you want to open a new window, you can use:


<%
url = "http://wherever.com/"
response.write("<script>" & vbCrLf)
response.write("window.open('" & url & "');" & vbCrLf)
response.write("</script>")
%>

If it is a form you're submitting to, you can use the following (either for a frame or a new window):

<%
url = "http://wherever.com/"
dest = "<form method=post action='" & url & "' target=framename>"
' for a new window:
'dest = "<form method=post action='" & url & "' target=_blank>"
response.write(dest)
%>
How do I validate a credit card number in ASP?

14,933 requests - last updated Thursday, July 27, 2000

While with ASP alone you can't verify that a credit card belongs to this person, that it isn't stolen, that the credit is

good or that the purchase price doesn't exceed the card's limit -- you can verify that it is a possible number before

bothering to waste transaction time with a true verification authority.

<%
function isCreditCard(cardNo)
isCreditCard = false
lCard=len(cardNo)
lC=right(cardNo,1)
cStat=0
for i=(lCard-1) to 1 step -1
tempChar= mid(cardNo,i,1)
d=cint(tempChar)
if lcard mod 2 = 1 then
temp=d*(1+((i+1) mod 2))
else
temp=d*(1+(i mod 2))
end if
if temp < 10 then
cStat = cStat + temp
else
cStat = cStat + temp - 9
end if
next
cStat = (10-(cStat mod 10)) mod 10
if cint(lC) = cStat then isCreditCard = true
end function

' **************** AS A TEST ****************


'
' 4111111111111111 should pass (test number)
' 4111111111111112 should fail
'
response.write("1:" & isCreditCard("4111111111111111") & "<br>")
response.write("2:" & isCreditCard("4111111111111112") & "<br>")

'
' *************** TRY YOUR OWN **************
'

%>
How can I mimic a client-side POST from ASP?

10,995 requests - last updated Friday, June 8, 2001

Often you need to POST information to an ASP or other script without relying on the user to click a Submit button.

There are a few ways to work around this:

1. Write a component. We have one that does this (among many other things); however, it is proprietary, and

as such I can't distribute source. I will tell you it is an ATL component and makes a low-level connection.

2. Use an existing HTTP component, such as AspHTTP or MSXML (described in Article #2173)

3. "Fake" a POST operation with client-side script, as in the following:

<form
method=post
action='<script>'
name='myform'>

<input
type=hidden
name='myname'
value='myvalue'>

</form>

<script>
window.onLoad = document.myform.submit();
</script>

Note that it's fairly easy to build such a form with ASP, simply by iterating through the incoming form

elements
How do I iterate through a form collection?

10,531 requests - last updated Sunday, October 28, 2001

It's kind of a pain to list all of the elements from a submitted form by referring to each one individually by name,

e.g.:

<%
response.write("a = " & request.form("a") & "<br>")
response.write("b = " & request.form("b") & "<br>")
response.write("c = " & request.form("c") & "<br>")
......
response.write("n = " & request.form("n") & "<br>")
%>

There's an easy way to manipulate this, particularly when you're troubleshooting and just want to write out all the

variables to the screen (or to a comment in the page). The following code will iterate (haphazardly, mind you)

through each form element:

<%
for each x in Request.Form
Response.Write("<br>" & x & " = " & Request.Form(x))
next
%>

And in JScript:

<%
for(f = new Enumerator(Request.Form()); !f.atEnd(); f.moveNext())
{
var x = f.item();
Response.Write("<br>" + x + " = " + Request.Form(x));
}
%>
What I mean by haphazardly is that, while this is a very easy way to get all of the elements in three lines, they will

not be in the order you expect... they'll be all over the place, and I have yet to see a valid explanation of how the

order is derived.

So another way to do this iteration actually preserves the order of the original form, by cycling through the form

items numerically (there is a count property of the form object). Here it is in VBScript:

<%
for x = 1 to Request.Form.count()
Response.Write(Request.Form.key(x) & " = ")
Response.Write(Request.Form.item(x) & "<br>")
next
%>

And in JScript:

<%
for (x = 1; x <= Request.Form.count(); x++)
{
Response.Write(Request.Form.key(x) + " = ");
Response.Write(Request.Form.item(x) + "<br>");
}
%>

[This technique also works for the QueryString and ServerVariables collections - in the ServerVariables collection,

this doesn't change anything, since they're already ordered by iteration.]

Personally, I would have chosen "name" and "value" over "key" and "item." Of course, I don't have as much

influence over Microsoft as some of my co-workers seem to think. :-)


How do I validate forms using server side script?

8,393 requests - last updated Sunday, February 25, 2001

Jay McVinney wrote an article on server side form validation for Wrox's ASPToday.com site. You can find it here, if

you're willing to shell out $50:

Professional Form Validation


Which is faster: Request("item") or Request.Form("item")?

6,933 requests - last updated Monday, September 16, 2002

According to Microsoft, you should ALWAYS use the complete name of the collection you are retrieving items from.

For more information, read the following resource in its entirety:

Request Object

A lot of people leave out the actual collection name, merely putting request("item") either out of laziness or because

they're not sure which collection the value will come from. In my opinion, it is better programming practice to either

(a) use one method exclusively, or (b) do the following in such cases where you can't avoid using multiple

submission methods:

<%
item = Request.QueryString("item")
if item = "" then
item = Request.Form("item")
end if
%>

(Note: this is more efficient than querying for Request.ServerVariables("REQUEST_METHOD"), since the

ServerVariables collection is a significant performance hit.)

The reason you should always use an explicit name is that if you don't specify, you can't be certain which collection

the variable is coming from (it also can be a much more significant performance hit to poll all of the collections

looking for your variable). Take the following example - play with each form, and see which collection is used as the

'default' in each case:


order.asp
--------

<%
response.cookies("HTTP_USER_AGENT") = "cookie"
%>
<form method=post action=order2.asp?HTTP_USER_AGENT=get>
<input type=hidden name=HTTP_USER_AGENT value=post>
<input type=submit value='form with all'>
</form>

<form method=post action=order2.asp?HTTP_USER_AGENT=get>


<input type=submit value='form without post'>
</form>

<form method=post action=order2.asp>


<input type=hidden name=HTTP_USER_AGENT value=post>
<input type=submit value='form without get'>
</form>

<form method=post action=order2.asp>


<input type=submit value='form with neither'>
</form>

order2.asp
----------

<%
response.write "The first value is from: " & _
request("HTTP_USER_AGENT")
%>

Note that in each case, the collection changes. If you had avoided using the cookie in the first line of the page, you

would have received the value in Request.ServerVariables("HTTP_USER_AGENT") only in the last case (where that

variable name was not used in the Form or QueryString collections).


So, always name the collection you are referencing, instead of letting the engine figure it out for you. At most, it's

15 more characters... and if you're doing it often enough, you could create a function for each collection to ease

your poor fingers.


Why does my input type=text value get truncated?

5,445 requests - last updated Tuesday, September 10, 2002

When you insert values into textboxes dynamically, you have to remember the same rules that hold true for basic

HTML. When you use a string, you must store it within quotes to prevent premature concatenation. :-)

<... value=<%=value%>>

Becomes:

<... value="<%=value%>">

One thing you want to be careful of is embedded quotes. You might try using ' or " as the delimiter, and eliminating

the other for possible entry (using client-side validation of course; the value is destroyed before you'd be able to

validate for it on the server side). If you have to allow both ' and ", you could consider using the rarely used "back-

apostrophe" (`). You can also try to user Server.HTMLEncode() on the value, before slipping it into the HTML

element.

If you do this:

<... value='<%="foo's bar"%>'>

This evaluates to:

<... value='foo's bar'>

And everything after 'foo' is ignored, because the browser interprets that as the end of the string.
How do I retrieve the name of the form that was submitted?

4,454 requests - last updated Sunday, July 9, 2000

The name of the form is not passed with the form collection. You can do one of two things:

1. Use a hidden element in each form that will go to the same page, this way you can determine the form that

was submitted. This should be sufficient, unless you don't have control over the submitting forms. If you

don't, you have to explain to the people who DO have control over them, that you need some way to

distinguish their requests.

2. Use Request.ServerVariables("HTTP_REFERER") to determine what page the request came from, which

should help you determine which form it was. HTTP_REFERER is unreliable, so I don't recommend this one

too highly.
Why does my form variable become 'value, value' instead of 'value'?

4,138 requests - last updated Tuesday, July 11, 2000

This is usually a case of multiple form fields on the previous page with the same name. For example, if you have a

form with the following:

<input type=text name=a value="whatever">


<input type=text name=a value="whatever else">

When you do a request("a"), it's actually going to return the following:

whatever, whatever else

If this happens, scan through your form looking for multiple instances of that input field name.
What is the size limit of a posted FORM field?

3,620 requests - last updated Thursday, February 21, 2002

Any single request element is limited to ~102kb. If you exceed this limit, you may get 80000009 or 80004005

errors.

See Article #2223 for more info on POST limitations.

In addition, Microsoft has a few workarounds listed in Q273482 (note the warnings regarding usage of the code).
What is the limit on Form / POST parameters?

3,062 requests - last updated Thursday, February 21, 2002

Unlike QueryString data, POSTed form data has a very high number of allotted characters. This is because the data

is transferred in the headers and not in the URL.

Note that there is no limit on the number of FORM elements you can pass via POST, but only on the aggregate size

of all name/value pairs.

While GET is limited to as low as 1024 characters, POST data is limited to 2 MB on IIS 4.0, and 128 KB on IIS 5.0.

Each name/value is limited to 1024 characters, as imposed by the SGML spec. Of course this does not apply to files

uploaded using enctype='multipart/form-data' ... I have had no problems uploading files in the 90 - 100 MB range

using IIS 5.0, aside from having to increase the server.scriptTimeout value as well as my patience! :-)

See Q260694 to learn how to adjust the limits of POST data (this deals with adding/modifying

MaxClientRequestBuffer in the registry).


How do I cause/prevent ENTER being used to submit a form?

3,041 requests - last updated Monday, March 26, 2001

Rather than try to reproduce it here, take a look at this great article:

http://ppewww.ph.gla.ac.uk/%7Eflavell/www/formquestion.html
How do I make one dropdown depend on another?

2,909 requests - last updated Monday, June 17, 2002

This is asked all the time. Often you want <SELECT> elements to populate based on previous selections. There are

many different solutions, based on what you want to do, and how much data you actually need to swap around. The

articles / samples below vary on a few things, mainly whether the re-populating is performed client-side or after a

round-trip to the server.

http://www.macromedia.com/support/ultradev/ts/documents/client_dynamic_listbox.htm

http://www.aspfree.com/authors/adrian/twocombotwoform.asp

http://www.atgconsulting.com/doublelist.asp

http://www.atgconsulting.com/triplelist.asp

http://www.caoxuan.com/cxk/webart/goodies/selopt1.html

http://www.mattkruse.com/javascript/dynamicoptionlist/

http://www.infinitemonkeys.ws/infinitemonkeys/articles/javascript/997.asp

Please let us know if you know of other resources / methods to accomplish this functionality.
What is the limit on QueryString / GET / URL parameters?

2,710 requests - last updated Tuesday, November 13, 2001

RFC 2068 states:

Servers should be cautious about depending on URI lengths above 255 bytes, because some older client or

proxy implementations may not properly support these lengths.

The spec for URL length does not dictate a minimum or maximum URL length, but implementation varies by

browser. On Windows: Opera supports ~4050 characters, IE 4.0+ supports exactly 2083 characters, Netscape 3 ->

4.78 support up to 8192 characters before causing errors on shut-down, and Netscape 6 supports ~2000 before

causing errors on start-up.

Note that there is no limit on the number of parameters you can stuff into a URL, but only on the length it can

aggregate to.

Keep in mind that the number of characters will be significantly reduced if you have special characters (e.g. spaces)

that need to be URLEncoded (e.g. converted to the sequence '%20'). For every space, you reduce the size allowed in

the remainder of the URL by 2 characters - and this holds true for many other special characters that you may

encode before sending the URL to the client.

Keep in mind, also, that the SGML spec declares that a URL as an attribute value (e.g. <a href='{url}'>) cannot be

more than 1024 characters. Similarly, the GET request is stored in the server variable QUERY_STRING, which can

have similar limitations in certain scenarios.

If you are hitting a limit on length, you should consider using POST instead of GET. POST does not have such low

limits on the size of name/value pairs, because the data is sent in the header, not in the URL. The limit on POST

size, by default, is 2 MB on IIS 4.0 and 128 KB on IIS 5.0. POST is also a little more secure than GET -- it's tougher

(though not impossible) to tinker with the values of POSTed variables, than values sitting in the querystring.

See Article #2223 for more information on using POST to overcome limitations on length.
When I'm uploading files, why can't I access the request.form collection?

2,500 requests - last updated Thursday, March 7, 2002

You will get errors from IIS in both of the following cases:

■ You try to access the form collection (request.form) after calling request.binaryread, or

■ You try to access the incoming binary stream after calling request.form.

The errors you receive are typically one of the following:

Request object error 'ASP 0206 : 80004005'


Cannot call BinaryRead after using Request.Form collection

or

Request object error 'ASP 0206 : 80004005'


Cannot call Request.Form collection after using BinaryRead

The two major commercial upload components have their own .form collection that you can call, and this is found in

their documentation:

ASPUpload

SA-FileUp

Dundas.Upload, a free upload control, also has this capability:

Dundas.Upload

If you're using one of the other components, contact the vendor.

If you wish to roll your own upload method(s), take a look at this helpful article:
http://msdn.microsoft.com/library/en-us/dnasdj01/html/asp0900.asp
How do I perform spell checking from a web page?

2,293 requests - last updated Monday, December 9, 2002

When a user enters data, it'd be nice if you could find possible typos in their text and offer suggestions. Here are

some articles / tools that will help you do that:

ASPFree.com

Chado SpellServer

Don's Easy Spell Checker

Planet Source Code (IE only)

TCP/IQ Spell Checker

Wintertree Spelling Server

XDE's Java Spell Checker

Note that some of these solutions require licenses for Microsoft Word.
When I have multiple submit buttons, how do I tell which was clicked?

2,037 requests - last updated Friday, January 25, 2002

You can use a hidden input type, coupled with a client-side handler that intercepts the submit.

<form method=post onsubmit="return false;"


name=form1>

<input type=text name=bob>

<input type=hidden name=buttonChoice>

<input type=button value="Choice 1"


onclick='setButton("Choice1");'
style='cursor:pointer'>

<input type=button value="Choice 2"


onclick='setButton("Choice2");'
style='cursor:pointer'>

</form>

<script>
function setButton(s)
{
document.form1.buttonChoice.value = s;
document.form1.submit();
}
</script>

<%
for each x in request.form
response.write "<p>" & x & " = "
response.write request.form(x)
next
%>
How do I disable certain FORM elements?

1,711 requests - last updated Thursday, November 1, 2001

You can use the DISABLED attribute to turn off input for elements, for example:

<INPUT TYPE=TEXT NAME=foo DISABLED VALUE='bar'>

One side effect is that this attribute is not recognized by Netscape, so those users will still be able to edit the text.

Also, FORM elements marked as "disabled" are removed from the collection, so the page you're posting to won't be

able to collect the value (a messy workaround would be to also place the value in a HIDDEN element).

A workaround for all of this is to use the READONLY attribute, for example:

<INPUT TYPE=TEXT NAME=foo READONLY VALUE='bar'>

This will solve both side effects mentioned above. However, some have noted that this doesn't change the

appearance of the field (it still *looks* editable). To do this also, you can add a style to the field (which, admittedly,

will only work in certain browsers):

<INPUT TYPE=TEXT NAME=foo READONLY VALUE='bar'


STYLE='color:#999999;background:#DEDEDE'>
How do I retrieve the text and the value from a <SELECT> element?

1,567 requests - last updated Monday, April 15, 2002

Let's say you have the following:

<form method=post action=page2.asp>


<select name=foo>
<option value='1'>Option 1
<option value='2'>Option 2
</select>
</form>

Many people want to know how to display "Option 1" on the resulting page, in addition to retrieving the value

parameter. I have heard of examples where they resorted to a database lookup based on the value, since the

SELECT element does not support passing the text, only the value. Here is a decent workaround:

<form method=post action=page2.asp>


<select name=foo>
<option value='1:Option 1'>Option 1
<option value='2:Option 2'>Option 2
</select>
</form>

Then in the receiving page:

<%
foo = Request.Form("foo")
foo = split(foo,":")
Response.Write "Value was " & foo(0) & "<br>"
Response.Write "Display was " & foo(1)
%>

You may want to choose a delimiter other than ":", for example if your text might contain that character. There

should always be a character you can choose as a delimiter that has little to no chance of ever being inside the
string.
How do I make form fields read-only?

1,559 requests - last updated Monday, January 7, 2002

Here are three ways to make form fields read only:

<input type=text READONLY value='Do not touch'>

<input type=text DISABLED value='Do not touch'>

<input type=text value='Do not touch' onfocus='this.blur();'>

The first two require "recent" browsers (left as an exercise to the reader); the third requires a JavaScript-enabled

browser.
Why won't my <TEXTAREA> display the data I passed to it from ASP?

1,467 requests - last updated Wednesday, February 20, 2002

There is a common misconception that the following code should work:

<TEXTAREA VALUE='<%="stuff"%>'></TEXTAREA>

A TEXTAREA element is a container, like a <TD> or <SPAN>, so you put contents BETWEEN the opening and closing

tags, as follows:

<TEXTAREA>
<%="stuff"%>
</TEXTAREA>
How do I disable IE's Autocomplete feature?

1,303 requests - last updated Thursday, June 27, 2002

While this isn't an ASP question per se, it is often requested by developers creating an ASP site. Sometimes you just

don't want IE to remember previous entries in an input field. You can turn this feature off as follows:

<INPUT TYPE="text" AUTOCOMPLETE="Off">

You can also turn it off at the form level, like so:

<FORM AUTOCOMPLETE="Off">
How can I programmatically interfere with the INPUT TYPE=FILE element?

1,269 requests - last updated Monday, January 7, 2002

When uploading a file, users are constantly asking if they can programmatically populate the path in an INPUT

TYPE=FILE element. The fact is, users must input this file themselves by typing it in manually or using the provided

Browse... button. Otherwise, it would be fairly trivial for malicious users to steal files from users' hard drives...
How do I pass x-y coordinates to ASP, after the user clicks an image?

1,249 requests - last updated Tuesday, October 30, 2001

Use an image input type as your submit button. With the following code:

<form method=post action=receiver.asp>


<input type=image name=coords width=300 height=100>
</form>

The receiving page can do this:

<%
Response.Write("The coordinates were: " & Request.Form("coords.x"))
Response.Write(", " & Request.Form("coords.y") & ".")
%>
How do I submit a form to a new window, with the control of window.open()?

780 requests - last updated Sunday, August 11, 2002

Many people have asked how they can submit a form to a new window. This is fairly trivial to accomplish; you can

just set up your form tag like this:

<form
target=_blank
action=whatever.asp>

However, some would like to submit their forms to a new window that they have a little more control over.

This is not really an ASP issue, as the solution could be used in any web-based technology (including plain HTML).

However, here is a way to submit a form to a new window, and have control over parameters like width, height,

toolbar, etc.:

<form name=form1
method=post
action=whatever.asp
target=myNewWin>

<input type=hidden name=foo value='bar'>

<input type=button
value=' Submit '
onClick='sendme();'>
</form>

<script>
function sendme()
{
window.open("","myNewWin","width=500,height=300,toolbar=0");
var a = window.setTimeout("document.form1.submit();",500);
}
</script>
Why do I get 'HTTP 405 - Resource Not Allowed' errors?

718 requests - last updated Monday, August 19, 2002

HTTP/1.1 Error
405 Method Not Allowed
The method specified in the Request Line is not allowed for the resource identified by the
request. Please ensure that you have the proper MIME type set up for the resource you are
requesting.
Please contact the server's administrator if this problem persists.

You can get this when your form doesn't have a name, or a method -- particularly in Netscape.

You can get this if you try to submit a form to an HTM, HTML or other 'static' page type.

You can get this when your form doesn't have an action parameter, or it is left blank, if the form is in the default

document *and* the user accessed the file as http://yoursite/yourfolder/ instead of

http://yoursite/yourfolder/default.asp. See Q216493 for more information (and not ethat you don't have to be using

a DTC for this symptom to appear).

If you are using Remote Scripting, see Q191276.

If you are using Visual InterDev's preview/design modes, switch to your browser. Don't use your editor to preview

your code, use the tool your users will be using!

If you are using Posting Acceptor to upload files, make sure IUSR has full permissions on cpshost.dll; or, better yet,

use a real upload solution (Article #2189).

You can get this if you have FrontPage Server Extensions installed, and the _vti_bin lacks 'execute' permissions. See

Q238461 for more information. Also, see Q206046 and Q229295 for other FrontPage-related articles.

A rare case is searching Option Pack 4.0 documentation (see Q186809).


How do I dynamically include files?

26,199 requests - last updated Saturday, August 10, 2002

One of the greatest obstacles faced by ASP developers is the ability to dynamically include files. Since #include

directives are processed BEFORE the ASP code, it is impossible to use if/else logic to include files.

Or is it?

Depending on what you are doing within your include files, and how many you are including, it IS possible to use

if/then logic to make use of includes. While it is not feasible for all situations, and it is often an inefficient solution, it

can occasionally be quite a handy workaround.

Let's start with two sample HTML files, 1.htm and 2.htm. For the sake of simplicity, they contain very simple code:

<!-- 1.HTM: -->


<font color=#ff0000>This is 1.htm</font>

<!-- 2.HTM: -->


<font color=#0000ff>This is 2.htm</font>

Now, let's set up some conditional includes! For this example, we'll assume you want to include the file 2.htm if your

page is passed a parameter of 2, otherwise include 1.htm. Here is an example of how you could accomplish this

task:

<%
if request.querystring("param")="2" then
%>
<!--#include file="2.htm"-->
<%
else
%>
<!--#include file="1.htm"-->
<%
end if
%>
Now try accessing the page in these three ways, and experiment with the results:

http://yourserver/file.asp?param=1

http://yourserver/file.asp?param=2

http://yourserver/file.asp

Of course you can perform this kind of include logic based on various conditions, such as the date, the time, or the

user's browser.

Please note that in the above example, BOTH include files are processed. So, the more options you have, the less

efficient this kind of solution will be. When the number of possible includes start getting a bit high, you could try

something like this:

<%
if request("param")="2" then
filespec = "2.htm"
else
filespec = "1.htm"
end if
filespec = server.mapPath(filespec)
scr = "scripting.fileSystemObject"
set fs = server.createobject(scr)
set f = fs.openTextFile(filespec)
content = f.readall
set f = nothing
set fs = nothing
response.write(content)
%>

The FileSystemObject is useful for many things, and it fits into the dynamic include paradox quite nicely.

IIS 5.0 / ASP 3.0 supports "dynamic includes" with Server.Execute. Here is a sample using the above scenario:
<%
if Request.QueryString("param")="2" then
Server.Execute("2.htm")
else
Server.Execute("1.htm")
end if
%>

If you have IIS 5.0 at your disposal (IIS 5.0 and ASP 3.0 will ONLY run on Windows 2000), look this method up in

the VBScript documentation. They can be handy for a quick and dirty solution to the dynamic include problem.

Please see Article #2006 for some of the common problems people have with these methods.
Where can I find info on working with files and FileSystemObject?

17,996 requests - last updated Tuesday, August 15, 2000

Here is Microsoft's starting point for FileSystemObject:

http://msdn.microsoft.com/library/en-us/script56/html/FSOoriFileSystemObject.asp

There are samples for creating, copying, deleting and renaming files, as well as reading and appending the text

within them.

If you are not running Windows 2000 or Windows XP, make sure you have the latest scripting engine before trying

any of these samples. Several methods and properties of the FileSystemObject were not available in the original

implementation.
How do I create / manipulate images from ASP?

11,954 requests - last updated Tuesday, September 24, 2002

There are a few components that allow you to do this; here are two of them:

ShotGraph

ASPImage

The following components are specialized for creating charts and/or graphs from ASP:

ASPGFX

ChartDirector

Chart FX

DundasChart

IntrChart

PopChart Image Server

The following free component will tell you the properties of a local image file:

ImageSize

Finally, you can determine the size of an image without a component.

http://www.learnasp.com/learn/graphicdetect.asp

I found this script a bit buggy with JPG files produced by certain filters in Photoshop. Also, thanks to Bryan O'Malley

and Thomas Honoré Nielsen, here is a correction of the ReadJpg function from the above link:
Function ReadJPG(file)
Const maxJpegSearch = 2048
Dim fso, ts, s, HW, nbytes, x, SOF
HW = Array("","")
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(Server.MapPath("/" & file), 1)
s = ts.Read(maxJpegSearch)
ts.Close
for x = 1 to Len(s) - 1
if Asc(Mid(s, x, 1)) = &hFF then
if Asc(Mid(s, x + 1, 1)) >= &hC0 AND _
Asc(Mid(s, x + 1, 1)) <= &hCF AND _
Asc(Mid(s, x + 1, 1)) <> &hC4 then
SOF = x
exit for
end if
end if
next
if SOF > 0 then
s = Mid(s, SOF + 5, 4)
HW(0) = HexToDec(HexAt(s,3) & HexAt(s,4))
HW(1) = HexToDec(HexAt(s,1) & HexAt(s,2))
else
HW(0) = -1
HW(1) = -1
end if
ReadJPG = HW
End Function

And here is how the HexToDec function should look:


<%
Function HexToDec(cadhex)
Dim n, i, ch, decimal
decimal = 0
n = Len(cadhex)
For i=1 To n
ch = Mid(cadhex, i, 1)
If isHex(ch) Then
decimal = decimal * 16
If isDigit(ch) Then
decimal = decimal + ch
Else
decimal = decimal + Asc(uCase(ch)) - Asc("7")
End If
Else
HexToDec = -1
End If
Next
HexToDec = decimal
End Function
%>
How do I sort a list of files?

8,650 requests - last updated Sunday, June 30, 2002

One of IIS' great built-in tools is FileSystemObject. Unfortunately, there is no native way to produce a file listing in

the order you specify. For example, consider the following code snippet:

<%
folder = "C:\"

set fso = server.createobject("Scripting.fileSystemObject")


set fold = fso.getFolder(folder)
for each file in fold.files
response.write file.name & "<br>"
next
set fold = nothing: set fso = nothing
%>

This produces a list of files in seemingly random order. It is not ordered by name, file size, type, date modified, date

accessed, archive, read-only, system, hidden, super hidden...

Here are three ways you can order your files alphabetically; the first uses a VBScript sort routine, the second uses a

JScript sort routine, and the third uses a database.

Here is the VBScript version (thanks for the help with this, Luke Magnus):

<%
folder = "C:\"

set fso = server.createobject("Scripting.fileSystemObject")


set fold = fso.getFolder(folder)
fileCount = fold.files.count
dim fNames()
redim fNames(fileCount)
cFcount = 0
for each file in fold.files
cFcount = cFcount + 1
fNames(cFcount) = lcase(file.name)
next
for tName = 1 to fileCount
for nName = (tName + 1) to fileCount
if strComp(fNames(tName),fNames(nName),0)=1 then
buffer = fNames(nName)
fNames(nName) = fNames(tName)
fNames(tName) = buffer
end if
next
next
for i = 1 to fileCount
content = content & fNames(i) & "<br>"
next
Response.Write content
%>

Here is the JScript sort version:

<script language='jscript' runat='server'>


// remember to double-up backslashes:
var folder = 'C:\\';

var fso = new ActiveXObject('Scripting.FileSystemObject');


var fold = fso.GetFolder(folder); filesArrayString = '';
for (files = new Enumerator(fold.files); !files.atEnd(); files.moveNext())
{
var thisFile = files.item();
thisFile=thisFile.name.toLowerCase();
filesArrayString+=thisFile+'/';
}
var lenString = filesArrayString.length-1;
filesArrayString = filesArrayString.substring(0,lenString);
var filesArraySplit = filesArrayString.split('/'); filesArraySplit.sort();
Response.Write(filesArraySplit.join('<br>'));
</script>
And here is the database version (you'll want to create a simple table, called fileTable, with a filename column,

varchar(255)):

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set fso = Server.CreateObject("Scripting.FileSystemObject")
set fold = fso.getFolder("c:\")
for each file in fold.files
sql = sql & "INSERT INTO fileTable(filename)"
sql = sql & " VALUES('" & file.name & "'); "
next
conn.execute(sql)
sql = "SELECT filename FROM fileTable ORDER BY filename"
set rs = conn.execute(sql)
do while not rs.eof
filename = rs("filename")
if left(filename,5) = "D0101" then
fileURL = server.urlEncode(filename)
response.write("<a href='Documents/" & fileURL
response.write("'>" & filename & "</a><br>")
end if
rs.movenext
loop
rs.close: set rs = nothing
conn.execute("DELETE FROM fileTable")
conn.close: set conn = nothing
%>

The bonus with the database version is that you can insert other elements (such as extension or DateLastModified),

and order in different ways (file type; creation date, ascending or descending; reverse alphabetical; etc...). A slightly

more efficient way to generate a list of files ordered in whatever way you like would be to use a recordset opened as

adOpenDynamic:
<%
CONST adFldIsNullable = &H00000020
CONST adVarChar = 200
CONST adDate = 7
CONST adOpenDynamic = 2
CONST adUseClient = 3

response.write "<table border=1 cellpadding=5 cellspacing=0" & _


" bordercolor=#CCCCCC style='font:8pt tahoma'>"

set fso = server.createobject("scripting.filesystemobject")


set folder = fso.getFolder("C:\")

if folder.files.count > 0 then


Set rs = Server.CreateObject("ADODB.Recordset")

With rs
.CursorLocation = adUseClient
.CursorType = adOpenDynamic
.Fields.Append "filename", adVarChar, 255, adFldIsNullable
.Fields.Append "filemod", adDate, 32, adFldIsNullable
.open

for each file in folder.Files


.AddNew
.Fields("filename") = file.Name
.Fields("filemod") = file.dateLastModified
.update
next

' list oldest first

.Sort = "filemod ASC"


.MoveFirst
response.write "<tr><th colspan=2 bgcolor=#CCCCCC>" & _
.sort & "</th></tr>" & _
"<tr bgcolor=#EFEFEF><th>Name</th>" & _
"<th>Modified</th></tr>"
do while not .eof
Response.Write "<tr><td>" & .Fields(0) & "</td>" & _
"<td>" & .Fields(1) & "</td></tr>"
.Movenext
loop

' list newest first

.Sort = "filemod DESC"


.MoveFirst
response.write "<tr><th colspan=2 bgcolor=#CCCCCC>" & _
.sort & "</th></tr>" & _
"<tr bgcolor=#EFEFEF><th>Name</th>" & _
"<th>Modified</th></tr>"
do while not .eof
Response.Write "<tr><td>" & .Fields(0) & "</td>" & _
"<td>" & .Fields(1) & "</td></tr>"
.Movenext
loop

' list alphabetically

.Sort = "filename ASC"


.MoveFirst
response.write "<tr><th colspan=2 bgcolor=#CCCCCC>" & _
.sort & "</th></tr>" & _
"<tr bgcolor=#EFEFEF><th>Name</th>" & _
"<th>Modified</th></tr>"
do while not .eof
Response.Write "<tr><td>" & .Fields(0) & "</td>" & _
"<td>" & .Fields(1) & "</td></tr>"
.Movenext
loop

.close
end with
response.write "</table>"
set rs = nothing
end if
set folder = nothing
set fso = nothing
%>
How do I make the filename correct for the client, when using binaryWrite?

8,025 requests - last updated Sunday, March 10, 2002

If you have an ASP file that dynamically produces files in binary for download, you've probably noticed that when the

client saves the file, it gets renamed to yourfile.asp instead of thefile.ext. Here's how you can avoid this... before

sending the response.binarywrite command, issue this:

<%
fn = "thisfile.ext"
Response.AddHeader "Content-Disposition","attachment;filename=" & fn
...
response.binarywrite(binarydata)
%>

Note that some 3rd-party download managers will still override this setting, and save the file as yourfile.asp. You

can try the utlities mentioned in Article #2232 to see if any suit your needs.
Why does FileSystemObject hang all of a sudden?

6,409 requests - last updated Tuesday, July 2, 2002

Likely, you (re-)installed or re-configured Norton Anti-Virus, and as a result, a call within ASP to OpenTextFile hangs

and/or other methods stop responding. This program has an option called 'script blocking' which, among other things,

prevents FileSystemObject from working on the local file system. With the default setting, Norton raises a 'prompt'...

and sits there waiting and waiting for a response from ASP (which it can obviously never get). To stop this from

happening, go to Norton's Options screen, click on 'Script Blocking' and uncheck 'Enable Script Blocking'...

Keep in mind you shouldn't be running client applications like this on a commercial web server... hopefully this issue is

only affecting your workstation!


There is more information available from Microsoft and Symantec on this issue:

Microsft KB #Q295375

Symantec KB #2001031311101006
How do I get the name of the current URL / page?

6,268 requests - last updated Thursday, August 15, 2002

This one is pretty easy, but there are two parts.

To retrieve the name of the current file, you can use any of these:

<%
Response.Write Request.ServerVariables("SCRIPT_NAME") & "<br>"
Response.Write Request.ServerVariables("PATH_INFO") & "<br>"
Response.Write Request.ServerVariables("URL") & "<br>"
%>

To make that path local (for example, to use with FileSystemObject), just apply the server.mappath() method to the

result.

To get the entire URL, including the http:// or https:// prefix, you can do this:

<%
prot = "http"
https = lcase(request.ServerVariables("HTTPS"))
if https <> "off" then prot = "https"
domainname = Request.ServerVariables("SERVER_NAME")
filename = Request.ServerVariables("SCRIPT_NAME")
querystring = Request.ServerVariables("QUERY_STRING")
response.write prot & "://" & domainname & filename & "?" & querystring
%>

To get the page name ONLY, use something like this:


<%
scr = Request.ServerVariables("SCRIPT_NAME") & "<br>"
if instr(scr,"/")>0 then
scr = right(scr, len(scr) - instrRev(scr,"/"))
end if
response.write scr
%>

Or, without the IF logic:

<%
scr = Request.ServerVariables("SCRIPT_NAME") & "<br>"
loc = instrRev(scr,"/")
scr = mid(scr, loc+1, len(scr) - loc)
response.write scr
%>

Now. If your file is an #INCLUDE within another file, the above scripts will produce the name of the CALLING file

(since the included file is first integrated into the calling script, then the ASP within it is all executed in the context of

the 'parent' file). One way you can work around this is to re-populate a current_filename variable before loading

each include file, for example:

<%
current_filename = "filetoinclude.asp"
%>
<!--#include file='filetoinclude.asp'-->

(And no, don't try passing current_filename as a variable to the #INCLUDE directive; see Article #2042.)

Then, in filetoinclude.asp:
<%
Response.Write "Current file: " & current_filename
%>

Of course, you could just as easily hard-code the filename inside of each include file. But I suppose that solution

would somewhat defeat the purpose of retrieving that information at least somewhat dynamically.
Why do I get 'Permission Denied' errors with FileSystemObject?

4,347 requests - last updated Saturday, January 20, 2001

Permission Denied, when dealing with the FileSystemObject, has to do with local file and folder permissions for the

anonymous user (IUSR_<machine_name>).

To open a text file forReading (1), IUSR_<machine_name> must have read access in the folder the file is located.

To open a text file forWriting (2) or forAppending (8), or to create a text file, IUSR_<machine_name> must have

read and write access in the folder.

Rather than alter the permissions for IUSR_<machine_name>, giving him/her access to various parts of your

machine, my recommendation is to first pursue placing any such text files within the web structure, or in any other

place that IUSR_<machine_name> already has access. The more you alter file- and folder-level permissions to suit

your application, the more open your system will be for attack... and the more maintenance you will have to do if

your application should ever move to a new server.


Can I rename a file using FileSystemObject?

3,988 requests - last updated Friday, March 8, 2002

Unfortunately, FSO does not have a "renameFile" method. However, there are two ways around this. The name

property of the GetFile method is read/write, so you can do this:

<%
set fso = server.createobject("scripting.filesystemobject")
set fs = fso.GetFile("c:\boot.ini")
fs.name = "c:\boot.old"
set fs = nothing
set fso = nothing
%>

You can also rename a file using FSO's movefile method. Simply enter the old name and the new name as

parameters, for example:

<%
Set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.moveFile "c:\boot.ini", "c:\boot.old"
Set fso = Nothing
%>

I strongly recommend writing a reverse script to change this file back before you reboot, or else use another

filename on your machine. (Hopefully, though, your IUSR doesn't have proper permissions for the above script to

work.)
Why do I get 'Path not found' errors with FileSystemObject?

3,059 requests - last updated Friday, June 14, 2002

This error is usually pretty self-explanatory. You tried to access a file/folder that doesn't exist, or you tried to access

a file that does exist but you specified the wrong folder. (This message is also incorrectly displayed when you to try

to access a text file or folder on another machine via a UNC share... the error message should be Permission Denied.

See Article #2168 for more info, if this is the situation.)

Some tips on avoiding this:

1. Make use of the folderExists and fileExists properties of FSO!

2. Use OpenTextFile with the "create" flag to true, instead of assuming the file exists (or using convoluted logic

to createTextFile if the file doesn't exist, and openTextFile if it does).

3. When folders are in the web structure, use server.mappath("/virtual/") to get the local location, since this

can be vary on different machines. This is an expensive call, so you might want to store the webroot

(server.mappath("/")) in an application variable instead of calling the mappath method every time you need

it.
How do I prevent people from 'leeching' my images?

2,476 requests - last updated Sunday, June 30, 2002

Many people have expressed concern that other users have tried to 'steal' or 'leech' their images, simply by

referencing them in their own HTML pages. For example, you can easily put the following line of code into your own

pages, hosted anywhere:

<IMG SRC=http://www.aspfaq.com/img/nld.jpg>

And of course my logo will show up on your page.

Here is one way to get around this, making it easy for you to host all of your images and not worry about others

being able to 'borrow' them. Place your image file(s) in a folder, either in an unknown spot in your webroot or

outside of your webroot altogether, and then use the following code (which plops c:\okay.gif into the browser if the

request came from the same domain, otherwise it returns c:\warning.gif - which can be as effective as you make it

in having people tear down their references to your image(s)):

<%
ref = lcase(Request.ServerVariables("HTTP_REFERER"))
if instr(ref, lcase("YOUR_DOMAIN_NAME"))>0 then
fn = "okay.gif"
else
fn = "warning.gif"
end if

FPath = "c:\" & fn


Set adoStream = Server.CreateObject("ADODB.Stream")
adoStream.Open()
adoStream.Type = 1
adoStream.LoadFromFile(FPath)
Response.BinaryWrite adoStream.Read()
adoStream.Close: Set adoStream = Nothing
Response.End
%>
Then simply reference this ASP file in your other pages:

<IMG SRC=imageFile.asp>

You may want to add other clauses for other domains your site may be referenced as, such as localhost, 127.0.0.1,

its 'real' IP address, etc. etc. You may also want to generate the IMG SRC tags using known parameters such as

width and height (for the tag itself) and the 'okay' filename (to pass via the querystring).

This method will definitely affect performance, so you will have to measure for your environment and decide whether

the tradeoff is acceptable.

Protecting the images themselves

Now, as we know, it's virtually impossible to prevent people from taking your images when they browse your web

site, e.g. saving them to their hard drive and re-using them. While they will always be able to use the PrintScreen

button (or one of the many shareware / commercial screen scrapers out there), there are several things you can do

to prevent the casual user from using the most common methods:

http://pubs.logicalexpressions.com/Pub0009/LPMArticle.asp?ID=41
Can I place a file on a user's hard drive without bothering them with a prompt?

2,327 requests - last updated Monday, January 7, 2002

NO.
How do I get a list of a folder's subfolders?

2,088 requests - last updated Thursday, November 1, 2001

Many people expect FileSystemObject to just return a list of subfolders and display them to the screen. It's almost

that easy:

<%
folderspec = server.mappath("/")

Set fso = CreateObject("Scripting.FileSystemObject")


Set fold = fso.GetFolder(folderspec)
for each subfolder in fold.subFolders
Response.Write(subfolder.name & "<br>")
next
set fold = nothing
set fso = nothing
%>

And here is a sample in JScript:

<script language=JScript runat=server>

var folderspec = Server.mapPath("/");

var fso = new ActiveXObject("Scripting.FileSystemObject");


var fold = fso.GetFolder(folderspec);
for (f = new Enumerator(fold.subFolders); !f.atEnd(); f.moveNext())
{
Response.Write(f.item() + "<br>");
}
var fold = null;
var fso = null;

</script>
Note that the JScript version returns the full folder path, while the VBScript version only returns the folder name

itself. You can alter the VBScript version to return the full path, by omitting the ".name" from the Response.Write

statement. You could also parse the folder name out of the string JScript returns. Or you could read the docs (the

way I clearly *haven't* done), and tell us the proper method of returning only the subfolder name in JScript.
Why do I get permissions errors after upgrading to Windows XP?

1,912 requests - last updated Monday, February 4, 2002

After switching from Win95, Win98 or WinME to Windows XP, you may find that previously functional ASP pages are

now choking on code that uses FileSystemObject or MS Access (usually 'Permission Denied' or 'Operation must use

an updateable query' errors). To straighten this out, you need to apply appropriate permissions for

IUSR_<machine_name> on the folder(s) you need to read/write with ASP.

In Windows Explorer, right-click the folder in question, hit Properties, and select the Security tab. If the Internet

Guest Account is not listed, click the Add... button and type IUSR_<machine_name> into the textbox, and click OK.

Now select the Internet Guest Account, and check the permissions appropriate. For most web applications, Read and

Write is sufficient. You may have to do this for individual files as well. One one of my work machines, Photoshop 6.0

saves files in a weird manner, so that IUSR cannot access them until I fix their permissions. Still investigating that

one.

Now, what if you don't have a Security tab?

Open up Windows Explorer, open the Tools menu, choose Folder Options, and go to the View tab. The last item in

the list is called "Use simple file sharing (recommended)" - which is actually NOT recommended if you want to get

any work done. Uncheck this box, click Apply and OK, and try the above steps again.

Oh gosh, you still don't have a security tab?

It's possible this was hidden from you by a group policy (perhaps the OEM set the machine up that way, or your

network admin doesn't trust you). Assuming you have appropriate permissions on the machine itselgf, go to Start,

Run... and type in "gpedit.msc" without the quotes. This launches the Group Policy Editor. Navigate to User Config /

Administrative Templates / Windows Components / Windows Explorer / Remove Security Tab. Read about this

setting before just changing it, and back up your system before applying any changes (just in case).

Ummm, Houston?

If those steps don't work, I'm at a loss. I don't run XP Home, and never will, so I'm not sure how much more digging

I'll be able to do on this issue.


Can I include a file in both server-side script and client-side script?

1,870 requests - last updated Wednesday, January 30, 2002

Often, ASP developers want to put common functions in a place accessible to both client-side and server-side

scripts. There are some challenges with this, including the fact that several server-side objects (such as Response,

Request) are not available on the client, much like several client-side elements (such as document, window and

msgbox) are not available on the server. But if you write a routine that's generic enough, it can make sense to have

it serve both the client-side and server-side scripts - reducing maintenance in the long run.

If you're using IIS 5.0 or better, you can achieve this simply as follows. Let's say you have a .js file called

'whatever.js':

function returnValue() { return "value"; }

Now you can call this from both server-side and client-side JScript like this:

<script language=jscript runat=server src=whatever.js></script>

<script language=jscript runat=server>


Response.Write("Server: "+returnValue()+"<br>");
</script>

<script language=jscript src=whatever.js></script>

<script language=jscript>
document.write("Client: "+returnValue());
</script>

Now, for those using IIS 4.0:

I'm going to provide an example of using a VBScript routine in server-side and client-side VBScript, as well as using

a JScript routine in server-side and client-side JScript. I'm not going to attempt to crosswire the languages, nor am I

going to try to deal with workarounds for swapping out document / response depending on the scope.
Here is an example of using a VBScript routine in client-side and server-side script. You'll need a file called

fixString.asp:

<%
function fixString(str)
fixString = replace(str,"Bob","the coder formerly known as Bob")
end function
%>

And another ASP file, with this code:

<!--#include file='fixString.asp'-->
<%
response.write fixString("Do you think that's okay with Bob?")
set fso = Server.CreateObject("scripting.FileSystemObject")
set fs = fso.OpenTextFile(server.MapPath("fixString.asp"))
f = fs.ReadAll()
fs.close: set fs = nothing: set fso = nothing

' we just replace the server-side delimiters with client tags


f = replace(f,"<" & "%","<" & "script language=vbscript>")
f = replace(f,"%" & ">","</script>")
response.write(f)
%>

<script language=vbscript>
msgbox fixString("I don't think Bob will mind."),64,"Bob"
</script>

Here is an example of using a JScript routine in client-side and server-side script. You'll need a file called

fixStringJS.asp:
<script language=JScript runat=server>
function fixString(str)
{
return str.replace("Bob","the coder formerly known as Bob");
}
</script>

And another ASP file, with this code:

<!--#include file='fixStringJS.asp'-->
<Script language=JScript runat=server>
Response.Write(fixString("Do you think that's okay with Bob?"));
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fs = fso.OpenTextFile(Server.MapPath("fixStringJS.asp"));
var f = fs.ReadAll();
fs.close(); var fs = null; var fso = null;

f = f.replace(" runat=server","");
Response.Write(f);
</script>

<script language=Jscript>
alert(fixString("I don't think Bob will mind."));
</script>

Now, this fictitious example doesn't deal with a serious limitation in JScript's implementation of replace - it only

replaces the *first* instance of the search string, not *all* instances (like VBScript). Not sure which language

implemented it wrong; according to the ECMA docs, JScript is correct. But I prefer the behavior of VBScript. I'll leave

it as an exercise to the reader to code a workaround in JScript.


Why do I get 'Invalid procedure call or argument'?

1,625 requests - last updated Tuesday, August 27, 2002

This often happens because you used a VBScript "friendly name" constant in place of its integer equivalent. Visual

Basic understands these friendly names, such as FileSystemObject's 'forAppending' and 'forWriting' constants.

An easy solution is to add this line to any include files you use in every page (or else add it at the top of every page,

if you don't have a common header in your application):

<%
Const ForReading = 1, ForWriting = 2, ForAppending = 8
%>

Another possible cause is trying to use a string operation (like Mid(), InStr(), Left() or Right()) on a NULL value. So

for example:

<%
response.write Left(rs("column"), 10)
%>

Should be:

<%
if len(rs("column")) > 0 then
response.write Left(rs("column"), 10)
else
response.write " "
end if
%>

If you're having these problems with NULL values coming out of a database, see Article #2150.

Another possibility is that you are coming over from JavaScript, or otherwise think that string lengths are 0-based.
The following code sample will cause this error:

<%
str = "foo"
response.write Mid(str, 0, 1)

%>

Similarly, passing a starting argument of 0 to the Instr() function will cause the same error. To solve this problem,

always start at 1 for string parsing in VBScript.


Why do I get 'Disk not ready' errors with FileSystemObject?

1,503 requests - last updated Saturday, January 20, 2001

Usually this means you used an absolute path to a drive that either doesn't exist, or is not currently enabled.

Common scenarios for this are removable media like ZIP drives or CD-Roms, network shares that are not always

available, or simply fat-fingering a drive letter in your ASP code (it's okay, it happens to all of us!).

Of course, it is possible that this error will also really mean what it says: that your hard disk has a failure of some

sort. If this is the case, you have more to worry about than a malfunctioning ASP page. <G>
How do I prevent people from 'leeching' my CSS or JS files?

1,454 requests - last updated Monday, April 15, 2002

I've heard a few people complain that external sites have linked to their CSS or JS files, which is both a waste of

bandwidth and a potential copyright violation. Unfortunately, there is no straightforward way to prevent this from

happening, and have fun trying to pursue it legally -- I was an expert witness in an Internet-related trial a few years

ago, and it's amazing how much crap a lawyer can get away with it simply because the judge has no technical savvy

whatsoever.

In any case, there are ways to prevent people from borrowing these file types, and it's very similar to the method

described in Article #2276, used to protect images from being swiped.

The basic concept is that you serve up the CSS or JS from ASP, and the ASP page checks the referer to make sure

it's on the same domain. So, for example, you can do this:

<SCRIPT LANGUAGE=javascript SRC=js.asp></SCRIPT>


<LINK REL=Stylesheet HREF=css.asp></SCRIPT>

Where js.asp may have the following code:

<%
ref = lcase(request.serverVariables("HTTP_REFERER"))
if instr(ref,"localhost")>0 then
response.write "document.write('HELLO');"
end if
%>

And css.asp might look like this:


<%
ref = lcase(request.serverVariables("HTTP_REFERER"))
if instr(ref,"localhost")>0 then
response.write "body { font-size:72pt }"
end if
%>

As with the images solution, note that this will be a bigger performance hit... and may affect how JS / CSS files are

cached.
How do I prevent that ugly red x when an image is missing from my server?

1,435 requests - last updated Monday, April 15, 2002

The 'red x' is a fairly telltale symptom of (a) a poorly maintained file system, or (b) poorly written HTML code. :-)

There is a way to prevent this nasty little red x from showing up. Read Article #2162, which describes how to set up

a custom 404 page to capture mistyped URLs and missing files. You can modify the example there to include the

following code:

<%
qs = Request.ServerVariables("QUERY_STRING")
if left(qs,4)="404;" then qs = right(qs,len(qs)-4)
if qs <> "" then
qsf = lcase(qs)
if right(qsf,4)=".gif" or right(qsf,4)=".jpg" or _
right(qsf,5)=".jpeg" or right(qsf,4)=".png" then
Server.Transfer("/404.gif")
' use Response.Redirect for IIS 4.0
end if
end if
%>

Now any bad request for an image file will result in the 404 graphic to be displayed. For example:

Just make sure that you have a file called /404.gif, or else the custom error page will go into an infinite loop.
How do I determine the owner of a file?

806 requests - last updated Tuesday, June 11, 2002

Here's a little script that will list out the files in a folder, and show the filename, type, size,

created/modified/accessed date, owner, and file attributes. This code has been tested on Windows 2000, XP

Professional and .NET Server.

<style>
body,td,th { font-family:tahoma;font-size:8pt }
</style>
<%
filesz = 0
fileown = 8

srv = request.serverVariables("SERVER_SOFTWARE")
if instr(srv,"/5.0")>0 then

filesz = 1
filecr = 6
fileac = 7
fileat = 4

elseif instr(srv,"/5")>0 or instr(srv,"/6")>0 then

filesz = 1
filecr = 4
fileac = 5
fileat = 6

end if

if filesz > 0 then

folder = "c:\"

response.write "<b>File listing for " & folder & "</b><p>"


response.write "<table border=1 cellpadding=5 cellspacing=0>"
response.write "<tr>"
response.write "<th>Filename</th>"
response.write "<th>Type</th>"
response.write "<th>Size</th>"
response.write "<th>Created</th>"
response.write "<th>Modified</th>"
response.write "<th>Accessed</th>"
response.write "<th>Owner</th>"
response.write "<th>Attributes</th>"
response.write "</tr>"

tds = "<td><nobr> "


tde = " </nobr></td>"

Set sh = Server.CreateObject("Shell.Application")
set fl = sh.Namespace(folder)

for each f in fl.Items


if not f.IsFolder then
p = f.path
fn = right(p, len(p)-instrRev(p,"\"))
response.write "<tr>" & _
tds & fn & tde & _
tds & f.type & tde & _
tds & fl.GetDetailsOf(f,filesz) & tde & _
tds & fl.GetDetailsOf(f,filecr) & tde & _
tds & f.modifyDate & tde & _
tds & fl.GetDetailsOf(f,fileac) & tde & _
tds & fl.GetDetailsOf(f,fileown) & tde & _
tds & fl.GetDetailsOf(f,fileat) & tde & _
"</tr>"
end if
next

set fl = nothing
set sh = nothing

response.write "</table>"
else
response.write "Unable to process results."
end if
%>

You may need to make sure that IUSR_YourMachine (or the authenticated user) has appropriate access to the folder

in question. Also, be aware that hidden or protected system files (such as boot.ini) won't be shown unless your

machine is wide open.

Thanks to "Reverend Brad" for pointing me to the .path property, which at least put aside the mystery of the

missing extensions that I encountered when initially putting together this article.

Here is a full listing of the GetDetailsOf elements - they are different on Windows 2000 than on Windows XP / .NET

Server.

Notice that some are particularly useful for Word or other MS Office documents. Note that Name doesn't always

contain the extension, just in case you try to use it and don't understand why you get inconsistent results.

# Windows XP / .NET Server Windows 2000

0 Name Name

1 Size Size

2 Type Type

3 Date Modified Date Modified

4 Date Created Attributes

5 Date Accessed Comment

6 Attributes Date Created

7 Status Date Accessed

8 Owner Owner
9 Author ???

10 Title Author

11 Subject Title

12 Category Subject

13 Pages Category

14 Comments Pages

15 Copyright Copyright

16 Artist Company Name

17 Album Title Module Desription

18 Year Module Version

19 Track Number Product Name

20 Genre Product Version

21 Duration Sender Name

22 Bit Rate Recipient Name

23 Protected Recipient Number

24 Camera Model Csid

25 Date Picture Taken Tsid

26 Dimensions Transmission Time

27 ??? Caller Id

28 ??? Routing

29 ??? Audio Format

30 Company Sample Rate


31 Description Audio Sample Size

32 File Version Channels

33 Product Name Play Length

34 Product Version Frame Count

35 ??? Frame Rate

36 ??? Video Sample Size

37 ??? Video Compression


How do I use FileSystemObject to create a file on the client?

538 requests - last updated Thursday, October 17, 2002

You don't. You can't use ASP to read, write, copy, move or delete files on the client's machine. ASP runs on the

server; what you need to handle this is a client-side technology, such as an ActiveX control or a signed Java applet.

If you want to get a file from the client's system to your server, you must ask them to upload it (see Article #2189).

And no, you can NOT pre-populate or otherwise programmatically interfere with the <input type=file> element.

If you want to get a file from the server to the client's system, you must ask them to download it (see Article

#2161). And no, you can NOT force the location -- or even the filename -- that the user will save with.
Why do I get an 'Invalid Path Character' error?

436 requests - last updated Saturday, September 21, 2002

In Windows, you can create a folder called c:\who,what\. You can also create a folder with 'bad' characters, e.g.

commas, in the name using FileSystemObject (of course, assuming sufficient permissions):

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CreateFolder "c:\who,what\"
set fso = nothing
%>

Now, when I try to do the following, I get an error:

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CreateFolder Server.Mappath("/who,what/")
set fso = nothing
%>

What's up with that? Here is the error I receive:

Server.MapPath() error 'ASP 0173 : 80004005'


Invalid Path Character
/<file>.asp, line <?>
An invalid character was specified in the Path parameter for the MapPath method.

The initial response I received from Microsoft was that the path passed to MapPath was perhaps 'protected' due to

possible conflicts with URLEncode or HTMLEncode. I commented that a comma is a perfectly valid URL and HTML

character, and that neither Server.URLEncode nor Server.HTMLEncode really altered its appearance. They said

they'd keep digging, and that they might even have to look at the code (gasp!).

So, while we wait for an official response, which I'm promised is forthcoming, I can suggest a workaround. Namely,
use Server.MapPath() to obtain the root, and then build out using "local" folder paths. For example:

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CreateFolder Server.Mappath("/") & "\who,what\"
set fso = nothing
%>

While I'm not optimistic this limitation of classic ASP will ever get fixed, I have to assert that if I can create a folder

with certain characters in the name in Windows or using FileSystemObject, I should be able to get the path of that

folder using Server.MapPath.

FWIW, here are the characters that cause the Server.MapPath method to fail:

CHR(0)
CHR(9)
CHR(10)
CHR(11)
CHR(12)
CHR(13)
CHR(32)
CHR(34) "
CHR(42) *
CHR(44) ,
CHR(58) :
CHR(60) <
CHR(62) >
CHR(63) ?
CHR(160)

Another case where you may come across this problem is something like the following:
<%
server.transfer("/file.asp?foo=1&bar=2")
%>

This is because server.transfer internally performs a server.mappath() (for some reason), and it can't take ? and &

characters...
How do I change the modified time of a file?

409 requests - last updated Tuesday, June 25, 2002

Unfortunately, this can't be done through ASP. The dateLastModified property of the FileSystemObject is read-only.

However, if it's a text-based file, you could read in the contents of the file, delete the file, and re-create it. That

would alter the create *and* modify time of the file, and might not be the desired result. Here is the code to do this

in VBScript:

<%
Sub Touch(filename)
set fso = Server.CreateObject("Scripting.FileSystemObject")
set fs = fso.openTextFile filename,1,true
f = fs.readAll()
fs.close: set fs = nothing
fso.deleteFile filename,true
set fs = fso.createTextFile filename,true
fs.writeline(f)
fs.close: set fs = nothing
set fso = nothing
End Sub

Call Touch(Server.MapPath("/somefile.htm"))
Call Touch("c:\somefile.txt")
%>

Another alternative is to use touch.exe, which you can get from the NT or Windows 2000 Resource Kit.
Why is 'the operation completed successfully' an error message?

341 requests - last updated Monday, August 19, 2002

I still don't get the logic behind this error message. When using the file system object or wscript shell, you may have

come across this error:

Server object error 'ASP 0177 : 800a004c'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

or

Server object error 'ASP 0177 : 800a0035'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

or

Microsoft VBScript runtime (0x800A0035)


File not found

Despite the "success" indicated by the text of the error message, this usually indicates that there was a permissions

problem accessing a file or folder with Scripting.FileSystemObject or Wscript.Shell. If the former, make sure that

IUSR_<machine> has appropriate privileges on scrrun.dll (found in your windows\system32 folder) and also

whatever resource you are attempting to access. If the latter, make sure IUSR_<machine> has appropriate

privileges on the wshom.ocx file (also in your system32 folder).

Also, make sure you are pointing to a valid file or folder!


Why do I get 800A0034 errors?

201 requests - last updated Monday, August 19, 2002

When using FileSystemObject in JScript, you might see this:

Server object error 'ASP 0177 : 800a0034'


Server.CreateObject Failed

One reason could be forgetting to double up backslash characters in local paths. Since \ is an escape character in

JScript, the following line:

var f = fso.openTextFile("c:\foo.htm");

Will actually get interpreted incorrectly, since JScript will be trying to render a "\f" character. So to fix, use the

following syntax:

var f = fso.openTextFile("c:\foo.htm");

If you are using VBScript, you might see this error:

Microsoft VBScript runtime error '800a0034'


Bad file name or number

One possible cause is forgetting to specify a filename in a FileSystemObject method, e.g.

fso.copyFile "c:\boot.ini", "c:\backup\"

Notice that no filename was specified in the second argument. To correct, make sure both the source and target file

has a name.

Another possible cause is attempting to use FileSystemObject to load a file from a URL. FileSystemObject only has
the ability to read *local* files, so if you want to process a text file that resides on another server, see Article

#2173.
Why do I get 800A0BBA errors?

134 requests - last updated Monday, August 19, 2002

When using ADODB.Stream to send files to the client, you may receive this error:

ADODB.Stream error '800a0bba'


File could not be opened.

Double-check your path -- be sure that the file exists and it is greater than 0 bytes. Make sure that

IUSR_machineName (or the authenticated user(s) / group(s)) has at least read access on the file. If the file is on a

network drive, please see Article #2168. If you are trying to stream a file from a URL (e.g. http://), use

response.redirect or server.transfer instead.


How do I retrieve a random file?

33 requests - last updated Tuesday, December 10, 2002

A lot of people use arrays or recordsets to pick a random file from a folder. Here is a slightly more efficient way...

instead of loading all of the filenames into a large array, just run through the list until you hit the random number.

<%
Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set fold = fso.GetFolder(Server.MapPath("folderName"))
Set fileset = fold.files
fileCount = fileset.count

if fileCount > 0 then

counter = 0
randomize
fileToPick = clng((rnd * fileCount) + 0.5)

for each file in fileset


if counter < fileToPick then
counter = counter + 1
else
randomFile = file.name
exit for
end if
next

response.write randomFile

else

response.write "Empty folder."

end if

set fileset = nothing


set fold = nothing
set fso = nothing
%>
How do I send e-mail from ASP?

44,765 requests - last updated Wednesday, September 11, 2002

There are several components that enable you to do this. You may already have one, if you have an SMTP server

installed alongside IIS - it's called CDONTS. Here is the documentation and an article for CDONTS:

http://msdn.microsoft.com/library/en-us/cdo/html/_denali_newmail_object_cdonts_library_.asp

Article #2026

There are also several other components available (if I missed any, let us know)

AspMail / ASPQMail

http://www.serverobjects.com/products.htm#Aspmail

Persits ASPEmail

http://www.aspemail.com/

MailListBot

http://www.maillistbot.com/

ATLMail

http://www.chizl.com/dev/c++/

SA-SMTPMail

http://www.softartisans.com/softartisans/smtpmail.html

Dundas Mailer

http://www.dundas.com/subFrame.asp?products/asp/Mailer/index.asp

EasyMail

http://www.quiksoft.com/products/

w3 Jmail

http://www.dimac.net/
HTMLMailer / HTMLMailerPlus

http://www.oopadelic.com/htmlmailer/

http://www.oopadelic.com/htmlmailerplus/

DevMailer

http://www.geocel.com/devmailer/

VSEmail

http://www.vsoft-tech.com.au/vsemail/readme.html

SimpleMail

http://simplemail.adiscon.com/en/

OCXMail

http://www.flicks.com/aspmail/

Zaks.POP3

http://www.zaks.demon.co.uk/code/cpts/pop/index.html

If you don't have access to CDONTS or CDO.Message, and can't install a COM object on your web server, you might

consider using a stored procedure to send mail from within SQL Server. See Article #2403 for a tutorial on

configuring your SQL Server for sending mail through Exchange or an external SMTP server.
How do I send e-mail with CDONTS?

30,121 requests - last updated Monday, November 18, 2002

To send an e-mail with Active Server Pages requires some kind of component. There are many third party

components available (see bottom of page), but one of the most readily available is the free Microsoft mail

component CDONTS, which ships with the Option Pack for WinNT 4.0.

Now, once you stop trying to comprehend it's name (I know I can't), it is a simple mail program to use.

CDO works by using the SMTP service in IIS, unless Exchange is installed, then it will just use Exchange's SMTP

system. Before continuing, make sure you have your SMTP service properly set up. You can check by using the

Microsoft Management Consol (MMC), or you can look to see if CDONTS.DLL is in your system32 directory.

To send e-mail from ASP, all you have to do is define the object and use the ".send" function.

<%
Set MailObj = Server.CreateObject("CDONTS.NewMail")
MailObj.Send "from@me.com", "to@me.com", "My Subject", "My Text"
Set MailObj = Nothing
%>

Wasn't that easy? If you need more properties, you can add them as necessary. But as it gets more complex, you

might find it easier to use the format that most people use:

<%
Set MailObject = Server.CreateObject("CDONTS.NewMail")
MailObject.From = "from@me.com"
MailObject.To = "to@me.com"
MailObject.Subject = "Subject Text Here"
MailObject.Body = "Body Text Here"
MailObject.CC = "someoneElse@somewhere.com"
MailObject.Send
Set MailObject = Nothing
%>
Sending an Attachment

<%
Set MailObject = Server.CreateObject("CDONTS.NewMail")
attFile = "c:\attachments\StandardPolicy.txt"
attName = "Policy.txt"
MailObject.From = "from@me.com"
MailObject.To = "to@me.com"
MailObject.Subject = "Subject Text Here"
MailObject.Body = "Body Text Here"
MailObject.AttachFile attFile, attName
MailObject.Send
Set MailObject = Nothing
%>

Problems

If you are having difficulties with CDONTS, see the following reference:

http://msdn.microsoft.com/library/en-us/cdo/html/_denali_newmail_object_cdonts_library_.asp

If you get "Permission Denied" errors, try switching "run in separate memory space" off and on again. Also see these

KB articles:

Q286301, Q228465, and Q197619

If you get "The system cannot find the path specified" errors, you may need to (re)install the SMTP service. For

details, see the following KB article:

Q235681

Windows XP

If you are trying to run CDONTS.NewMail on WinXP, you might get one of the following errors:
Invalid class string
' or
ActiveX component can't create object: 'CDONTS.NewMail'
' or
The "SendUsing" configuration value is invalid.

This is because Microsoft is using a new progID of the DLL used to send mail through CDO (CDONTS.NewMail is

being shuffled out). To deal with this, you can start using the new code style:

<%
sch = "http://schemas.microsoft.com/cdo/configuration/"

Set cdoConfig = Server.CreateObject("CDO.Configuration")

With cdoConfig.Fields
.Item(sch & "sendusing") = 2 ' cdoSendUsingPort
.Item(sch & "smtpserver") = "<enter_mail.server_here>"
.update
End With

Set cdoMessage = Server.CreateObject("CDO.Message")

With cdoMessage
Set .Configuration = cdoConfig
.From = "from@me.com"
.To = "to@me.com"
.Subject = "Sample CDO Message"
.TextBody = "This is a test for CDO.message"
.Send
End With

Set cdoMessage = Nothing


Set cdoConfig = Nothing
%>

You can handle it this way also -- thanks to Siegfried Weber for the advice -- including the CDO type library so you
don't have to use the schema URL/namespace, and this also allows you to use the named CDO constants:

<!--METADATA TYPE="typelib" UUID="CD000000-8B95-11D1-82DB-00C04FB1625D"


NAME="CDO for Windows 2000 Library" -->

<%
Set cdoConfig = Server.CreateObject("CDO.Configuration")

With cdoConfig.Fields
.Item(cdoSendUsingMethod) = cdoSendUsingPort
.Item(cdoSMTPServer) = "<enter_mail.server_here>"
.Update
End With

Set cdoMessage = Server.CreateObject("CDO.Message")

With cdoMessage
Set .Configuration = cdoConfig
.From = "from@me.com"
.To = "to@me.com"
.Subject = "Sample CDO Message"
.TextBody = "This is a test for CDO.message"
.Send
End With

Set cdoMessage = Nothing


Set cdoConfig = Nothing
%>

Or you can use the workaround of copying cdonts.dll from a Windows 2000 machine and regsvr32'ing it on the XP

machine. I have not tested this method, so try it at your own risk. If it works, it may -- at least in the short term --

be the better solution. But you should plan to migrate your code eventually, because hosts running .NET Servers are

not likely to be willing to register obsolete DLLs.

If you switch to the new code technique, the advantage is that it works on Windows 2000... so you can migrate your

code gradually. (If you are using Windows 2000, you should start using CDO.Message and migrating your code.)

There is a comprehensive article here -- describing all of the methods and properties, and showing a few code
samples:

http://msdn.microsoft.com/library/en-us/cdosys/html/_cdosys_imessage_interface.asp

And here is a good starting point:

http://msdn.microsoft.com/library/en-us/cdosys/html/_cdosys_messaging.asp

Last resort...

If CDONTS continues to frustrate you, check out Article #2119 for a thorough list of alternative SMTP components.

Your web host almost certainly supports at least one of them. If they don't, they should.

Another alternative, if you're using SQL Server, is to send the mail from within the database. Your ASP page can

pass parameters to a stored procedure that sends the mail without having to use CDO or a custom COM object on

your web server. See Article #2403 for a quick tutorial.


How do I put carriage returns into an e-mail?

7,640 requests - last updated Sunday, August 13, 2000

If you are using HTML mail, you can use HTML carriage returns. For example, if the body is coming from a textarea,

you'll want to use the following to put HTML carriage returns in:

<%
' ...
body = request.form("textareabody")
body = replace(body,vbCrLf,"<br>")
' ...
%>

If you are using plain text, then you just need to insert the VbCrLf constant to generate carriage returns. For

example:

<%
' ...
body = "Hello Aaron," & vbCrLf & vbCrLf & "How are you?"
' ...
%>
How do I validate an e-mail address?

2,744 requests - last updated Tuesday, January 8, 2002

You can use ASP to validate the *format* of an e-mail address, using a function like the following:

<%
function isEmail(car)
dim lencar
lencar = len(car)

isEmail = false 'assume error

if instr(car, "@") <= 1 or instr(car, ".") <= 1 or lencar < 6 then


exit function
end if

dim cs, j, k, c, c1
cs = 0

for j = 1 to lencar
c = mid(car, j, 1)
if c ="@" then
cs = cs + 1
end if
next

if cs <> 1 then
exit function
end if

dim dotArray, lenDotArray, lenDot, lenAt


dotArray = split(car, ".")

lenDotArray = (ubound(dotArray) - lbound(dotArray)) + 1


lenDot = dotArray(lendotArray - 1)
lenAt = mid(car,instr(car,"@") + 1)
if len(lenDot) > len(lenAt) then
exit function
end if

for k = 1 to lencar
c1 = ucase(mid(car, k, 1))

cnd1 = c1>="A" and c1<="Z"


cnd2 = isNumeric(c1)
cnd3 = c1="_" or c1="-" or c1="@"
cnd4 = c1="." or c1="~"

if not (cnd1 or cnd2 or cnd3 or cnd4) then


exit function
end if
next

isEmail = true
end function
%>

However, it is not so simple to test whether the e-mail account is actually valid. For that, you may want to generate

some random string or a key, e-mail it to the user, and have them enter it into your interface.
Why does my CDONTS mail hang out in the queue or pickup folders?

2,353 requests - last updated Friday, July 19, 2002

Here are some suggestions for resolving this issue:

■ if your mail is not leaving the queue folder, check out Q273644

■ if your mail is not leaving the pickup folder, and you are getting smtpsvc Event ID: 535 ("Virtual Server :

The drop directory , for * could not be created.") in your event log, see Q288538

■ make sure SMTP Virtual Server is running in Internet Services Manager

■ check the event log for SMTP Service events

■ turn on logging for your SMTP server, to see if the mail is even being attempted to be sent to the server

■ make sure your smarthost and FQDN are configured correctly

■ make sure your smarthost allows you to relay unconditionally (some require authentication)

■ make sure your server can resolve the ip of your smarthost name - try using the IP of your smarthost to see

if this is the issue

■ restart the SMTP Service (or IIS altogether), and if that fails reboot the server, to see if this clears up the

backlog

■ make sure you are sending from / to a valid address / domain

■ check any .rtr files in the \queue\ folder - these can be caused by various mistakes in code or config

■ check the server itself by setting up outlook express on the machine, using the same settings, and

attempting to send mail

■ if the machine is behind a firewall/router, make sure it has access to port 53


Why can't ASP handle 80,000 e-mails?

2,030 requests - last updated Friday, November 22, 2002

If you're sending out bulk e-mail to 80,000 people, ASP is not the tool you want to use. You probably want to

schedule an application that handles this in the background, instead of relying on a request/response-based

technology.

If you are using CDONTS, you may have come across 80040020 or 80070020 errors, in which case you'll want to

check out Q181697 ...

Simply put, ASP scripts aren't meant to run for as long as it can take to send out 80,000 individual e-mails. It may

work if you send, say 800 e-mails with 100 BCCs each, and use a mail object that supports queueing. But that's still

a lot of strain on your web server, your SMTP server, and your network in general. You can see an example of using

CDONTS and WSH to schedule blocks of e-mails in Q221495. If your distribution lists are getting this large, perhaps

it's time to consider enlisting the help of listserv software and/or services.

If you are running SQL Server, and have control over the server, you might consider installing XPSMTP. This way,

you can schedule a job to break apart large chunks of your task, without relying on ASP and without having to go

through the hassles of configuring SQL mail and a MAPI account on the SQL Server box. I have implemented this

extended stored procedure with great success; see Article #2403 for information on its usage.
Can I get CDO messages to return a read receipt?

1,754 requests - last updated Monday, November 18, 2002

Thanks to David Tabaka for providing the insight into this answer.

You can add a return receipt to an existing piece of CDONTS code with the following enhancement (marked in

bold):

<%
Set MailObject = Server.CreateObject("CDONTS.NewMail")
MailObject.From = "?FromAddress?"
MailObject.To = "?ToAddress?"
MailObject.Value("Return-Receipt-To") = "?ReceiptAddress?"
MailObject.Subject = "?Subject?"
MailObject.Body = "?Body?"
MailObject.Send
Set MailObject = Nothing
%>

Apparently, this is how you set up a read receipt with CDO.Message, but it doesn't seem to work in my

environment:

<!--METADATA TYPE="typelib" UUID="CD000000-8B95-11D1-82DB-00C04FB1625D"


NAME="CDO for Windows 2000 Library" -->

<%
Set cdoConfig = Server.CreateObject("CDO.Configuration")

With cdoConfig.Fields
.Item(cdoSendUsingMethod) = cdoSendUsingPort
.Item(cdoSMTPServer) = "<enter_mail.server_here>"
.Update
End With

fld1 = "urn:schemas:mailheader:disposition-notification-to"
fld2 = "urn:schemas:mailheader:return-receipt-to"

Set cdoMessage = Server.CreateObject("CDO.Message")

With cdoMessage
Set .Configuration = cdoConfig
.From = "from@me.com"
.To = "to@me.com"

.Fields(fld1) = "valid@somewhere.com"
.Fields(fld2) = "valid@somewhere.com"

.Subject = "Sample CDO Message"


.TextBody = "This is a test for CDO.message"
.Send
End With

Set cdoMessage = Nothing


Set cdoConfig = Nothing
%>

Keep in mind that this won't necessarily tell you that the user has actually read and understood the message, only

that they've opened it; and, also, that both Outlook and Outlook Express (and perhaps other clients as well) support

the ability to turn off read receipts (either per instance or globally).

In any case, here are the other CDO constants (and their header values) that are applicable to a CDONTS message,

using the .Value() property.

cdoApproved approved

cdoComment comment

cdoContentBase content-base

cdoContentDescription content-description

cdoContentDisposition content-disposition
cdoContentId content-id

cdoContentLanguage content-language

cdoContentLocation content-location

cdoContentTransferEncoding content-transfer-encoding

cdoContentType content-type

cdoControl control

cdoDisposition disposition

cdoDispositionNotificationTo disposition-notification-to

cdoDistribution distribution

cdoExpires expires

cdoFollowupTo followup-to

cdoInReplyTo in-reply-to

cdoLines lines

cdoMessageId message-id

cdoMIMEVersion mime-version

cdoNewsgroups newsgroups

cdoOrganization organization

cdoOriginalRecipient original-recipient

cdoPath path

cdoPostingVersion posting-version

cdoReceived received

cdoReferences references
cdoRelayVersion relay-version

cdoReturnPath return-path

cdoReturnReceiptTo return-receipt-to

cdoSummary summary

cdoThreadIndex thread-index

cdoXMailer x-mailer

cdoXref xref

cdoXUnsent x-unsent
Why do CDONTS messages end up in the badmail folder?

1,467 requests - last updated Friday, July 19, 2002

If you are getting .bdr files in the badmail folder, this means the mail could not be sent for some reason. You might

see an error like this, if you open up one of the .bdr files:

Unable to deliver this message because the following error was encountered:
This message is a delivery status notification that cannot be delivered.
Specific error code was 0xC00402C7

The most common cause is that you've either omitted the address in the From or To properties, or included an

invalid e-mail address (see Q267859 - fixed in Windows 2000 Service Pack 2)

You've reinstalled the IIS SMTP service (see Q290290), or your SMTP service isn't properly configured (see

Q265621)

You are using Smart Host and a DNS server that does not support TCP queries (see Q276347)

You are attempting to run Microsoft Commercial Internet System on Windows 2000 (see Q258918)
How do I prevent my links from wrapping in an e-mail?

1,291 requests - last updated Monday, September 9, 2002

Let's say your URL is:

http://www.wherever.com/myfolder/mypage.asp?id=1&page=2&frank=3&bob=43521

In most e-mail readers, this link will be active (clickable) but probably won't work correctly, because the mail

program (and/or the mail server) might force the text to wrap to the next line after a certain number of characters

(usually 72 or 76). So, what can you do to prevent this from happening?

Make a shorter URL

If your host headers and/or DNS entries are set up correctly, the first thing you can do is remove 4 characters in

www. so the link becomes:

http://wherever.com/myfolder/mypage.asp?id=1&page=2&frank=3&bob=43521

Additionally, you can think about using a shorter subfolder and/or ASP page name, e.g.:

http://wherever.com/myf/myp.asp?id=1&page=2&frank=3&bob=43521

With IIS 5+, you can consider using a subfolder where the page you want to go to is the default page, so that the

following would work:

http://wherever.com/myf/?id=1&page=2&frank=3&bob=43521

Maybe you could use shorter variable names in your querystring:


http://wherever.com/myf/?i=1&p=2&f=3&b=43521

Finally, if it's acceptable, you can use an the IP address, which may or may not shorten the domain name part of the

URL.

http://204.3.20.8/myf/?i=1&p=2&f=3&b=43521

So that takes it from 73 characters to 42!

Use a shorter "stub" link that redirects

If you can't modify the actual page that handles the incoming traffic, you could build a "stub" file like that redirects:

<%
qs = request.servervariables("QUERY_STRING")
response.redirect("http://www.wherever.com/myfolder/mypage.asp?" & qs)
%>

For IIS 4+, place the stub file in your root or in a subfolder, so your link would become:

http://wherever.com/stub.asp?id=1&page=2&frank=3&bob=43521
or
http://wherever.com/rd/stub.asp?id=1&page=2&frank=3&bob=43521

For IIS 5+, you can use the subfolder method above, and place a default page to do the redirect -- so your link

would become:

http://wherever.com/rd/?id=1&page=2&frank=3&bob=43521

Then, combining that with some of the techniques from above:


http://204.3.20.8/rd/?i=1&p=2&f=3&b=43521

www.tinyurl.com and similar services

About 10 seconds at www.tinyurl.com yielded a much shorter link than any of the above methods:

http://tinyurl.com/imh

The only concern I have is longer term -- how long will a free service (a) remain free, and/or (b) keep those links

around?

You may want to look at sites like www.shorturl.com and www.makeashorterlink.com, which offer similar services -

albeit a bit less convenient. The former requires registration by the person creating the link, and the latter shows

their page and logo before redirecting the user(s) following the link.

Use HTML format

So what do you do if you can't control the format and location of the pages on the server, and can't use a service

like tinyurl.com? If your clients accept it, you can format your e-mail as HTML, so that the link will surely not wrap.

You should make sure that your content is easy enough to read for those that have disabled HTML, are using a web

interface that doesn't allow HTML through, or are using a mail reader without HTML support (e.g. Pine). In cases

where you can offer alternate content, you should tell the user that they may have to re-construct the link. In

addition, the latest build of my chosen mailreader allows me to turn off HTML completely (in both preview pane and

full view); I've also recently started playing with a macro that strips the HTMLbody portion of an incoming Outlook e-

mail -- so I can even stop reading Comic Sans MS, which one of my co-workers insists on using. That messiness,

combined with the fact that many people turn off HTML to prevent web bugs etc., leads me to encourage you to do

anything you can to use the above methods *before* going the HTML route. AS more people learn of the methods to

disable HTML mail, the HTML solution will work in a smaller and smaller percentage of your audience.

Okay, enough chatter, here's some code that will help you construct HTML e-mail from a variety of mail objects

available in ASP. If you know of another mail object that supports HTML format, please let us know and we'll add a

code sample.
<%

' set up

CONST FromAddress = "you@you.com"


CONST FromName = "Your Name"
CONST ToAddress = "them@them.com"
CONST ToName = "Their Name"
CONST Subject = "The subject of the message"
CONST url = "http://www.wherever.com/myfolder/mypage.asp?id=1&page=2&frank=3&bob=43521"

CONST SMTPServer = "your.mailserver.com"

' only required for CDO.Message method:


CONST cdoURL = "http://schemas.microsoft.com/cdo/configuration/"

htmlbody = "<html><body>Please <a href='" & url & "'>register</a>.</body></html>"


textbody = "Please register:" & vbCrLf & url

' for CDO.Message:

set cdoM = CreateObject("CDO.Message")


set cdoC = CreateObject("CDO.Configuration")

Set cdoF = cdoC.Fields


With cdoF
.Item(cdoURL & "sendusing") = 2
.Item(cdoURL & "smtpserver") = SMTPServer
.Item(cdoURL & "smtpconnectiontimeout") = 10
.Update
End With

With cdoM
Set .Configuration = cdoC
.From = FromAddress
.To = ToAddress
.Subject = Subject
.HTMLBody = htmlbody

' alternate for non-HTML-aware:

.TextBody = textbody
.Send
End With

Set cdoM = Nothing


Set cdoS = Nothing
Set cdoF = Nothing

' for ASPMail / ASPQMail:

Set ASPMailer = Server.CreateObject("SMTPsvg.Mailer")


With ASPMailer
.RemoteHost = SMTPServer
.ContentType = "text/html"
.FromName = FromName
.FromAddress = FromAddress
.AddRecipient ToName, ToAddress
.Subject = Subject
.BodyText = htmlbody
.SendMail
End With
Set ASPMailer = Nothing

' for ASPEmail:

Set ASPEMailer = Server.CreateObject("Persits.MailSender")


With ASPEmailer
.Host = SMTPServer
.IsHTML = True
.FromName = FromName
.From = FromAddress
.AddAddress ToAddress, ToName
.Subject = Subject
.Body = htmlbody
.Send
End With
Set ASPEMailer = Nothing

' if you have a registered copy of ASPEmail 4.5, you can provide
' multipart content, so that non-HTML-aware readers can have an
' alternate version:

Set RegASPEMailer = Server.CreateObject("Persits.MailSender")


With RegASPEmailer
.Host = SMTPServer
.IsHTML = True
.FromName = FromName
.From = FromAddress
.AddAddress ToAddress, ToName
.Subject = Subject
.Body = htmlbody
.AltBody = textbody
.Send
End With
Set RegASPEMailer = Nothing
%>

For more information on sending HTML mail with CDO.Message, see Q286431...
How do I send e-mail from SQL Server?

1,144 requests - last updated Monday, December 2, 2002

SQL Server has the ability to send mail without being triggered from ASP. So, if you have an application that needs to

e-mail users once a month, or on their birthday, or to send you details every day about what went on in the database

that day, you can set up a job that wakes up and sends e-mail. Similarly, you can call extended stored procedures to

send mail on demand (e.g. every time a stored procedure fires).

First off, make sure your SQL Server Mail account is set up correctly - see

http://www.allisonmitchell.com/Articles/mail.htm, http://www.mssqlserver.com/articles/sqlmail_p1.asp and Q263556

(this part is not a lot of fun, because it requires a MAPI client on the server, and a valid Exchange account for the

MSSQLServer service).

If you are still having problems configuring or using SQL Mail, see if any of the following articles help: Q274330,

Q279867, Q293422, Q315886, Q321183.

If you can not (or do not wish to) configure SQL Mail, you can use CDO (see Q312839) or XP_SMTP_SendMail (from

http://www.sqldev.net/xp/xpsmtp.htm). I prefer the XP_SMTP_SendMail procedure over both CDO and SQL Mail.

XP_SMTP_SendMail is an extended stored procedure that will send mail through any valid SMTP server you specify,

and supports HTML formatting. This makes sending mail very flexible, especially if your SQL Server is not a part of a

domain, or you're not running Exchange - and you don't have or don't want to install CDO on your database

server(s).

To fire off e-mail on demand

Let's say you want to send an e-mail from within a stored procedure called foo. You can do it like this:

With XP_SendMail:
CREATE PROCEDURE foo AS
BEGIN
SET NOCOUNT ON
-- do some other actions
EXEC xp_SendMail
@recipients='you@you.com',
@message = 'foo was fired '+CONVERT(VARCHAR, GETDATE()),
@subject = 'foo was fired.'
END

or you can do it conditionally:

CREATE PROCEDURE foo AS


BEGIN
SET NOCOUNT ON
-- do some other action
IF @@ROWCOUNT > 0
BEGIN
EXEC xp_SendMail
@recipients='you@you.com',
@message = 'foo was fired ' +
CONVERT(VARCHAR, GETDATE()) +
CHAR(13) + CHAR(10) +
CONVERT(VARCHAR, @@ROWCOUNT),
@subject = 'foo was fired.'
END
END

With XP_SMTP_SendMail:
CREATE PROCEDURE foo AS
BEGIN
SET NOCOUNT ON
-- do some other actions
EXEC xp_SMTP_SendMail
@TO = 'you@you.com',
@from = 'someone@somewhere.com',
@message = 'foo was fired '+CONVERT(VARCHAR, GETDATE()),
@subject = 'foo was fired.',
@server = 'smtp.yourdomain.com'
END

or you can do it conditionally:

CREATE PROCEDURE foo AS


BEGIN
SET NOCOUNT ON
-- do some other action
IF @@ROWCOUNT > 0
BEGIN
EXEC xp_SMTP_SendMail
@TO = 'you@you.com',
@from = 'someone@somewhere.com',
@message = 'foo was fired ' +
CONVERT(VARCHAR, GETDATE()) +
CHAR(13) + CHAR(10) +
CONVERT(VARCHAR, @@ROWCOUNT),
@subject = 'foo was fired.',
@server = 'smtp.yourdomain.com'
END
END

If you need to send an attachment, you can simply add the following parameter:

@attachments = 'c:\attachment.txt'

For plenty of other information on using XP_SendMail, see http://tinyurl.com/1d29.


And XP_SMTP_SendMail is fairly well documented at http://www.sqldev.net/xp/xpsmtp.htm.

To schedule mail

Now, let's say you have a stored procedure that checks a table for birthdays, and e-mails a happy birthday to all

matches. It might look like this (this assumes XP_SMTP_SendMail is the method you're using):

CREATE TABLE dbo.birthdays


(
fullname VARCHAR(128),
email VARCHAR(128),
birthdate SMALLDATETIME
)
GO

INSERT birthdays VALUES('AB', 'ab@nonexists.com', '19740201')


INSERT birthdays VALUES('CB', 'cb@nonexists.com', '19870123')
INSERT birthdays VALUES('TC', 'tc@nonexists.com', '20011227')
GO

-- and include your own address, with today's date, for testing!

CREATE PROCEDURE dbo.proc_happyBirthday


AS
BEGIN
SET NOCOUNT ON

-- if you have more than 80 matches on any


-- possible day, you may want to use a cursor
-- instead - this will only handle 8000 chars.

DECLARE @BCCList VARCHAR(8000)


SET @BCCList = ''

-- concat the BCC list for people with birthdays


-- of today. Note that this job won't run on
-- February 29th of non-leap years, so you may
-- want to add logic for that...

SELECT @BCCList = @BCCList + ';' + COALESCE(email, '')


FROM Birthdays
WHERE
CONVERT(CHAR(8), birthdate, 112)
= CONVERT(CHAR(8), GETDATE(), 112)

-- trim off leading ';' character from concatenation

SET @BCCList = SUBSTRING(@BCCList, 2, LEN(@BCCList))

EXEC XP_SMTP_SendMail
@TO = 'you@you.com',
@BCC = @BCCList,
@from = 'someone@somewhere.com',
@subject = 'Happy Birthday to you!',
@message = 'You belong in a zoo!',
@server = 'smtp.yourdomain.com'
END
GO

Currently, you might be invoking this stored procedure manually, or having a scheduled ASP / VBS script execute the

stored procedure through ADO. Wouldn't it be nice to have the database take care of this, without involving other

machines and code?

Make sure SQL Server agent is running. In Enterprise Manager, expand the server in question, and expand the

Management node. Make sure SQL Server Agent is started.


If it is not running, right-click it and hit Start. Right-click and hit properties, and on the advanced tab, make sure SQL

Server Agent is set to Auto-restart if that option is available:


Next, you will need to create a job. Under SQL Server agent, right-click Jobs and choose 'New Job...' You will see the

following dialog:
On the General tab, name the job (e.g. SendBirthdayNotices).

On the Steps tab, click New... and enter 'Step 1' into the Step Name: box. Switch the database dropdown to the

database where your stored procedure lives, make sure 'Transact-SQL' is selected, and enter 'EXEC

proc_happyBirthday'.

On the Schedules tab, click New Schedule... and enter 'Schedule 1' in the Name: box. Make sure 'enabled' is checked,

and click the 'Change...' button. Set up a schedule that makes sense... e.g. daily at noon or daily at 5:00 am. The

interface may look a bit daunting but it's pretty simple to configure.
Now, you can monitor your job's progress in the jobs view to see last/next run dates, and right-click the job itself to

see the job history. You can also right-click the job and start it now (e.g. if you created it AFTER the scheduled run

time but still want it to run today).


Please let us know if you have any questions or comments about this article.
Where can I get more information about configuring and using CDO / CDONTS?

853 requests - last updated Friday, July 19, 2002

Microsoft has a couple of knowledge base articles on this topic.

Q293800 Information on configuring your SMTP server for CDO/CDONTS

Q286421 Information on testing your SMTP server manually

Here are some other resources for CDO / CDONTS outside of aspfaq.com:

CDOLive

Slipstick.com

15 Seconds

IISFaq I

IISFaq II

ASPIN.com I

ASPIN.com II

ASPFAQs.com

4guysFromRolla.com

And of course, if you are having SMTP-related problems, the experts in microsoft.public.inetserver.iis.smtp_nntp

should be able to help.


Can I use a remote SMTP server with CDONTS.NewMail?

701 requests - last updated Tuesday, July 30, 2002

CDONTS.NewMail only supports local smtp servers, so you can't set up a remote server to handle your mail from

this object. If you are unable or unwilling to configure the web server to support outgoing SMTP mail, you will need

to use another product. In Windows 2000 and above, you can use CDO.Message as follows:

<%
myMailServer = "smtp.yourdomain.com"

sch = "http://schemas.microsoft.com/cdo/configuration/"
Set cdoConfig = Server.CreateObject("CDO.Configuration")
cdoConfig.Fields.Item(sch & "sendusing") = 2
cdoConfig.Fields.Item(sch & "smtpserver") = myMailServer
cdoConfig.fields.update

Set cdoMessage = Server.CreateObject("CDO.Message")


Set cdoMessage.Configuration = cdoConfig
cdoMessage.From = "from@me.com"
cdoMessage.To = "to@me.com"
cdoMessage.Subject = "Sample CDONTS NewMail"
cdoMessage.TextBody = "This is a test for CDO.Message"
cdoMessage.Send
Set cdoMessage = Nothing
Set cdoConfig = Nothing
%>

If you don't have access to CDO.Message (e.g. running NT 4.0), see Article #2119 for a list of 3rd party COM objects

that you can use instead.


Why does CDO.Message give me an 8004020f error?

492 requests - last updated Thursday, June 27, 2002

When switching from CDONTS to CDO.Message, when using code like the following (as described in Article #2026):

<%
sch = "http://schemas.microsoft.com/cdo/configuration/"
Set cdoConfig = Server.CreateObject("CDO.Configuration")
cdoConfig.Fields.Item(sch & "sendusing") = 2
cdoConfig.Fields.Item(sch & "smtpserver") = "<enter_mail.server_here>"
cdoConfig.fields.update

Set cdoMessage = Server.CreateObject("CDO.Message")


Set cdoMessage.Configuration = cdoConfig
cdoMessage.From = "from@me.com"
cdoMessage.To = "to@me.com"
cdoMessage.Subject = "Sample CDONTS NewMail"
cdoMessage.TextBody = "This is a test for CDONTS message"
cdoMessage.Send
Set cdoMessage = Nothing
Set cdoConfig = Nothing
%>

You might come across the following error:

error '8004020f'
The event class for this subscription is in an invalid partition
/<file>.asp, line <line>

It is not clear where this error message comes from; it's not even in Microsoft's Knowledge Base or MSDN Library.

However here are some things you can try to alleviate the problem:

1. Make sure the SMTP server allows anonymous (non-authenticated) relaying. Otherwise, you'll have to use a

mail component that supports SMTP authentication, like ASPEmail.


2. Check if the problem is specific to the domain name(s) used in the e-mail addresses of the recipients. For

example, some users have complained that they can send to users on their own domain only; others have

said that they can send to any domain except their own.

3. If you have a proxy or firewall, make sure the web server is set up to correctly pass through it, that the

SMTP server knows about it, and that the proxy allows access to port 25.

4. Try using a SendUsing value of 1 (pickup) instead of 2 (port). E.g. the following line:

cdoConfig.Fields.Item(sch & "sendusing") = 2

Becomes

cdoConfig.Fields.Item(sch & "sendusing") = 1


Why do I get C00402CE / C00402C7 errors?

453 requests - last updated Monday, August 19, 2002

If using CDONTS.NewMail, you might have come across this error, from files in your badmail folder:

Unable to deliver this message because the follow error was


encountered: "Error is processing file in pickup directory."

The specific error code was 0xC00402CE.

Check permissions on the mailroot folder. IUSR_MachineName, or the authenticated user(s)/group(s), needs to have

write permissions on several of its subfolders.

Try running the site / application in a different application protection level. This is not a fix, obviously, but a bandaid

in case the situation is dire.

Another alternative is to use CDO.Message instead (which you'll eventually have to do anyway, since

CDONTS.NewMail has been deprecated -- and no longer ships with Windows XP / .NET Server). See Article #2026

for information on using CDO.Message.

If this is the error message you received:

Unable to deliver this message because the follow error was


encountered: "This message is a delivery status notification that
cannot be delivered.".

The specific error code was 0xC00402C7.

Check to make sure that your SMTP service is running and that your CDONTS code sets proper and valid from and to

addresses. There have been several reported problems about sending mail through CDONTS when the domain is the

same as the smart host, or not the same as the smart host, and these seem to always be alleviated by using

CDO.Message or a third-party component, which uses an SMTP server as opposed to the local Exchange system.
How do I alter the priority of a CDO message?

270 requests - last updated Monday, July 8, 2002

Using CDO.Message, you can do this:

<%
sch = "http://schemas.microsoft.com/cdo/configuration/"
Set cdoConfig = Server.CreateObject("CDO.Configuration")
cdoConfig.Fields.Item(sch & "sendusing") = 2
cdoConfig.Fields.Item(sch & "smtpserver") = "<enter_mail.server_here>"
cdoConfig.fields.update

Set cdoMessage = Server.CreateObject("CDO.Message")


Set cdoMessage.Configuration = cdoConfig
cdoMessage.From = "from@me.com"
cdoMessage.To = "to@me.com"
cdoMessage.Subject = "Sample CDONTS NewMail"
cdoMessage.TextBody = "This is a test for CDONTS message"
cdoMessage.Importance = 2
cdoMessage.Send
Set cdoMessage = Nothing
Set cdoConfig = Nothing
%>

Note:

2 = High Importance

1 = Medium Importance

0 = Low Importance

I haven't investigated whether this property is available with CDONTS.NewMail. Since it is being phased out, I think

my priority on that issue = 1 or less. :-)


Why do I get 80090020 errors?

160 requests - last updated Monday, August 19, 2002

When using CDONTS.NewMail, you might see the following error:

Error '80090020'
An internal error occurred.

CDONTS has always been a bear to set up correctly, and has many configuration settings which cause errors in

different environments. The recommended fix for this error is to use the newer CDO.Message instead, since

CDONTS.NewMail has been deprecated, and no longer ships with Windows XP / .Net Server. You can see sample

code for CDO.Message in Article #2026 (under 'Windows XP').


Why does CDO.Message give me 80040222 errors?

145 requests - last updated Saturday, September 21, 2002

CDO.Message.1 error '80040222'


The pickup directory path is required and was not specified

If you're using IIS 6.0, search %WINDIR%\system32\inetsrv\MetaBase.XML for the following line:

PickupDirectory=

Verify that the path exists, and has appropriate permissions. Alter the value if it is not set correctly.

On previous versions of IIS, the pickup directory is usually c:\inetpub\mailroot\pickup -- so make sure that this

path exists and has appropriate permissions.

If you are trying to send SMTP mail through exchange, make sure you set the cdoSendUsing property to

cdoSendUsingExchange (3). Otherwise, the SMTP service might be expecting a local pickup directory which has

never been configured.

If all else fails, try re-installing the SMTP service.


Why do I get 80040108 errors?

138 requests - last updated Monday, August 19, 2002

When using CDO, you might see this error:

error '80040108'

This can happen if you try to read or write a CDONTS property or a CDO configuration property after the mail has

been sent. Allegedly, these parameters are destroyed immediately after the message is sent, so they cannot be

accessed afterward - yielding the above error (which maps to CdoE_INVALID_OBJECT).

This can also happen if you do something like this:

<%
' ...
objCdo.body = "foo" & _
objCdo.send
%>

If you don't end that string correctly, the CDO object is going to try and append the send method to the body, which

of course is going to confuse the heck out of it. :-) So check your code for little items like that...

In addition, if you are using CDONTS.NewMail, consider using CDO.Message instead (see Article #2026).
Why do I get 8004020A errors?

137 requests - last updated Monday, August 19, 2002

When using CDO.Message, you may see the following error:

Error 8004020A
The SMTP server name is required, and was not found in the configuration source

You either left out the smtpserver option on CDO.Configuration, or didn't spell it in lower case (see Q265527). For

more information on using CDO.Message, see Article #2026.


Why do I get 8000900F errors?

137 requests - last updated Saturday, August 17, 2002

When you use CDONTS.NewMail and include the constant CDOVBS.INC, you may come across this error:

Error '8009000f'
Object already exists.

Some suggestions:

1. use CDO.Message instead of CDONTS.NewMail (see Article #2126);

2. instead of including CDOVBS.inc, use CONST statements in your own script to declare only those variables

you need;

3. avoid naming your object 'objMail'


Why does CDO.Message give me 80040213 errors?

136 requests - last updated Friday, September 13, 2002

If you copied the CDO.Message code from Article #2026 verbatim, you will probably get this error:

CDO.Message.1 error '80040213'


The transport failed to connect to the server.

Make sure you replace "<enter_mail.server_here>" with "yourserver.yourdomain.com" or "your.ip.address.octets"...


Could I get a little help with dates?

21,210 requests - last updated Tuesday, September 17, 2002

(1) Entering/comparing dates in a SQL Query

Make sure your date is a valid date. You can do this in VBScript using the isDate() function. Syntax is:

<%
if isDate(dateVar) then
' do something
else
' do something else
end if
%>

A SQL query might look like this:

NOTE: If you are using Access, you need to surround dates with pound signs (#). With most other databases,

including SQL Server 7, dates are surrounded with apostrophes (') and are treated like strings.

<%
' *** SQL Server:
sql = "SELECT field FROM table WHERE datefield > '" & dateVar & "'"

' *** for Access:


' sql = "SELECT field FROM table WHERE datefield > #" & dateVar & "#"

' *** for a stored procedure:


' sql = "EXEC Proc_name @dateField='" & dateVar & "'"
%>

If you only want records with datefields in the last n days, you can do something like this:
<%
n = 5

' *** SQL Server:


sql = "SELECT field FROM table WHERE DATEDIFF([d],GETDATE(),datefield)<" & n
%>

If you want records that fall between two dates (inclusive), you can do this:

<%
sql = "SELECT field FROM table WHERE datefield BETWEEN '" & dateVar1 & "' AND '" &
dateVar2 & "'"
%>

Another tip... don't name datetime fields with reserved words like DATE or TIME.

(2) Dealing with mm/dd/yyyy vs. d/m/yy vs. m/d/yy [...]

Use yyyy-mm-dd format for all dates when passing to the database. Then the database won't care which way it's set

up internally, the default locale (or the current user's regional settings) on the database machine, the default locale

(or current user's regional settings) of the IIS machine passing dates through ASP, and the date that the user

entered manually. Here is a quick example of converting ASP's date to yyyy-mm-dd format:

<%
dateVar = year(date) & "-" &_
left("00",2-len(month(date))) & month(date) &_
"-" & left("00",2-len(day(date))) & day(date)
%>

Of course, it will be up to you that dates entered by the user are in the correct format. No code can determine

whether the user who typed in 2/3/01 actually meant Febraury 3rd or March 2nd - it can only determine in which

format the application developer expects entries to be made.

See Article #2260 for more information on using yyyy-mm-dd format.


(3) Comparing/determining dates

VBScript has many useful date functions that can help you with many issues. One problem I had on a project was

running a loop from the first day of the PREVIOUS month to today. The DateAdd() function helped with this

immensely.

First I moved back a month by adding -1 months to today's date:

<%
lastmonththisday = dateadd("m",-1,date())
%>

Then I subtracted from that date the number of days that had passed that month (which would bring you back to

the last day of the previous month), and added one:

<%
lastmonthfirstday = dateadd("d",-day(date())+1,lastmonththisday)
%>

Now I put that together in a for loop that created a table, with each day from the beginning of last month to today

(VBScript's for loop works with dates excellently):

<%
response.write("<table>")
lmfd = dateadd("d",-day(date())+1,(dateadd("m",-1,date())))
for i = lmfd to date()
response.write("<tr><td>" & formatdatetime(i,1) & "</td></tr>")
next
response.write("</table>")
%>

I then threw in some logic to color the weekends with a different color:
<%
response.write("<table>")
lmfd = dateadd("d",-day(date())+1,(dateadd("m",-1,date())))
for i = lmfd to date()
bg = "#ffffff"
if weekday(i)=1 or weekday(i)=7 then bg = "#ffffcc"
response.write("<tr><td bgcolor=" & bg & ">" & formatdatetime(i,1) & "</td></tr>")
next
response.write("</table>")
%>

There is a more comprehensive date tutorial at learnASP.com:

http://www.learnasp.com/learn/datetime.asp

If you are using J(ava)Script, see the following index at Merlyn:

http://www.merlyn.demon.co.uk/js-dates.htm
How do I delimit dates for inserting/updating a database?

8,962 requests - last updated Monday, February 25, 2002

With SQL Server (and most other PL/SQL-based DBs):

<%
sql = "insert into table(datefield) values('1999-06-05')"
%>

With Access:

<%
sql = "insert into table(datefield) values(#1999-06-05#)"
%>

(In any database platform, you should try to use yyyy-mm-dd format. See Article #2260 for more details.)
SQL Server: How do I select time only from a DATETIME field?

5,407 requests - last updated Sunday, September 17, 2000

You have two options that come to mind straight away.

One is to use the CONVERT function in conjunction with specific style numbers, to convert the date value into a

specific type of string. I like using 114, because you can simply change the number of characters returned to

increase accuracy. For example, for HH:MM:

SELECT
CONVERT(CHAR(5), DateField, 114)
FROM
table
[WHERE ... ]

Will produce the following:

11:45

For HH:MM:SS:

SELECT
CONVERT(CHAR(8), DateField, 114)
FROM
table
[WHERE ... ]

Will produce the following:

11:45:37

And for HH:MM:SS:mmm:


SELECT
CONVERT(CHAR(12), DateField, 114)
FROM
table
[WHERE ... ]

Will produce the following:

11:45:37:623

Depending on your application, you may need any of the above accuracies. Usually, though, to-the-minute is

sufficient.

The other option is to use DATEPART to concatenate the time yourself. This is a bit messier, but is useful, for

example, if you only want the minutes and are not concerned about the actual hour. I can't think of a practical use

for this, but one must exist.

SELECT
DATEPART(MINUTE, DateField)
FROM
table
[WHERE ... ]

If you want to build an entire time string on your own, you could do this:

SELECT
CONVERT(VARCHAR(2),DATEPART(HOUR, DateField)) + ':' +
CONVERT(VARCHAR(2),DATEPART(MINUTE, DateField))
FROM
table
[WHERE ... ]
Now, you might find that minutes less than 10 will result in weird padding; for example, if you were going to use this

technique for building a time string on your own, you could end up with a result as follows:

5:3

This would be 5:03, but could obviously be misconstrued by the user. Here is how I work around this scenario:

SELECT
CONVERT(VARCHAR(2), DATEPART(HOUR, DateField)) + ':' +
CASE
WHEN DATEPART(MINUTE, DateField) < 10 THEN
'0'+CONVERT(VARCHAR(2),DATEPART(MINUTE, DateField))
ELSE
CONVERT(VARCHAR(2),DATEPART(MINUTE, DateField))
END
FROM
table
[WHERE ...]
Why does JavaScript's document.lastModified() not work in ASP files?

5,401 requests - last updated Sunday, October 28, 2001

ASP is compiled when the user requests the file, so browsers report the time the HTML was generated, not when the

ASP file was saved. Here is some code

VBScript:

<%
thisfile = Request.ServerVariables("SCRIPT_NAME")
thisfile = Server.MapPath(thisfile)
set fso = Server.CreateObject("Scripting.FileSystemObject")
set fs = fso.getfile(thisfile)
dlm = fs.datelastmodified
set fs = nothing: set fso = nothing
Response.Write("Last modified: ")
Response.Write(formatdatetime(dlm,1) & " " & formatdatetime(dlm,3))
%>

JScript:

<script language=jscript runat=server>


var thisfile = Request.ServerVariables("SCRIPT_NAME");
thisfile = Server.MapPath(thisfile);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fs = fso.GetFile(thisfile);
var dlm = fs.DateLastModified;
Response.Write("Last modified: " + dlm);
</script>

Of course, when you add this script to a file and save it, the dateLastModified value will become "now()" by

definition.

If you have a common include file, you could put this code in THAT file without changing the calling file. See Article
#2072 if you want the last modified time of the include file instead.
Can I get millisecond accuracy in ASP?

3,732 requests - last updated Monday, February 25, 2002

VBScript does not support such fine granularity within formatdatetime, datediff, or dateadd.

But there may still be a way to do what you want, depending on your goal.

If you want to count the number of milliseconds between two dates, there are very few scenarios where you could

make this work in VBScript. Perhaps if two datetime values are stored in SQL Server, including milliseconds, and you

retrieved them using a string... parsing it out and doing all the math yourself (yuck). If that were the case, you

could just as easily do the DATEDIFF within SQL Server, and then you wouldn't have to do the math in VBScript. You

can do this as follows:

SELECT numMs = CAST(DATEDIFF(MS, dt_1, dt_2) AS VARCHAR(32)) FROM <tbl>


-- or if you just want a datetime to display in ASP with milliseconds:
SELECT numMs = CAST(dt AS VARCHAR(32))

JScript does support a getMilliseconds() method, which gets the milliseconds from the current time:

<script language=javascript runat=server>


var dt = new Date();
var h = dt.getHours(), m = dt.getMinutes();
var s = dt.getSeconds(), ms = dt.getMilliseconds();
Response.Write(h + ":" + m + ":" + s + "." + ms);
</script>

So it may be possible that you can use Jscript to get millisecond accuracy. Certainly if you define two dates within a

script, and you want to know how many milliseconds passed between them, you can do custom math functions

which will have to vary calculations depending on how many minutes, seconds etc. have elapsed. But the question

is, how valuable is this information?

If you have milliseconds stored in SQL Server, you can get these values to the ASP page simply by adding a

DATEPART clause to your query, and returning it as a string. For example:


DECLARE @dt CHAR(23)
SET @dt = '2002-02-25 17:07:27.400'
CREATE TABLE #dt (d DATETIME)
INSERT #dt VALUES(@dt)
SELECT d, DATEPART(MS,d) FROM #dt
DROP TABLE #dt

Of course, once you get that value back to ASP, the greatest utility you will get from it is dumping it to the screen as

text.
How do I display time in military format?

3,027 requests - last updated Sunday, October 21, 2001

There are at least three options for this.

One is to change regional settings to display time in military format. I don't like to do this because it can break

existing code, and can change depending on who is logged into the server (if anyone).

The second is to use string formatting. Here is an example:

<%
ft = formatdatetime(time(),3)
response.write "Standard time:<p>" & formatdatetime(ft,3)
if right(ft,2)="PM" then
t = split(ft,":")
milhour = clng(t(0))
if clng(left(ft,2))<12 then milhour = milhour +12
mtime = cstr(milhour) & ":" & t(1)
mtime = mtime & ":" & left(t(2),2)
elseif clng(left(ft,2))=12 then ' this handles midnight only
mtime = "00:"
mins = datepart("n",ft)
secs = datepart("s",ft)
mtime = mtime & left("00",2-len(mins)) & mins
mtime = mtime & left("00",2-len(secs)) & ":" & secs
else
mtime = left(ft,len(ft)-3)
end if
response.write "<p>Military time:<p>" & mTime
%>

The third is to use SQL Sevrer to convert it for you:


<%
response.write "Standard time:<p>" & time()
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
sql = "SELECT CONVERT(CHAR(8),(CONVERT(DATETIME,CURRENT_TIMESTAMP,113)),114)"
set rs = conn.execute(sql)
response.write "<p>Military time:<p>" & rs(0)
rs.close: set rs = nothing
%>

The advantage with SQL is you could change it to CHAR(12) and get millisecond accuracy (the above scripts only get

down to the second).


Given two dates, how do I determine an age?

1,755 requests - last updated Monday, August 26, 2002

There are a few considerations here. Most people funnel into big mathematical equations, dividing the datediff in

days by 365.333333333 and doing all kinds of logic to equate that to an age. In addition, we need to be wary of

leap years and treat those cases differently. While a leap year baby's true age might technically be in the single

digits, the more important piece of data (usually) is that x number of years have passed since they were born. Here

is an example in VBScript:

<%
' use DateSerial(y,m,d) to avoid locale issues
date1 = DateSerial(1974,2,24)
date2 = DateSerial(year(date), month(date), day(date))

' make sure we have a valid date!


if date2 >= date1 then

' determine if leapYearBaby


if month(date1) = 2 and day(date1) = 29 then
leapBaby = true
end if

' get absolute number of years


ageInYears = cint(datediff("YYYY", date1, date2))

' get date1's month and day in terms of date2's year


date1alt = dateadd("yyyy", ageInYears, date1)

if date1alt > date2 then


' their birthday hasn't hit yet in date2's year
ageInYears = ageInYears - 1
end if

if leapBaby = true then


' need to format output slightly
yearsPassed = ageInYears
ageInYears = ageInYears \ 4
end if

response.write "Age: " & ageInYears


if leapBaby then response.write " (" & yearsPassed & " years since birth)"
else
response.write "Invalid date."
end if
%>

And here is an example in T-SQL (note that this example does NOT make special considerations for leap year

birthdays):

DECLARE @birthdate DATETIME, @bdThisYear DATETIME, @age INT

SET @birthdate ='1974-02-24'

SET @age = DATEDIFF


(
YEAR,
@birthdate,
CURRENT_TIMESTAMP
)

SET @bdThisYear = DATEADD


(
YEAR,
@age,
@birthdate
)

IF @bdThisYear > CURRENT_TIMESTAMP


SET @age = @age - 1

SELECT @age

Finally, here is a solution in Access, given a table named DOBs with a column named dob (with only date, and no
time information -- or time set to midnight):

SELECT
dob,
DateDiff
(
"yyyy",
CDate(dob),
Date()
)
- IIF
(
DateAdd
(
"d",
Day(dob)-1,
DateAdd
(
"m",
Month(dob)-1,
CDate(year(Date()) & "-01-01")
)
)
> date(),
1,
0
) AS age
FROM
dobs
How do I convert local time to UTC (GMT) time?

1,576 requests - last updated Monday, November 12, 2001

Many people have asked how they can convert local times to UTC format.

Current Date / Time

Converting the current time is relatively simple, assuming that your server is set up correctly (proper time

zone, and observes daylight savings time if appropriate). This is because the registry stores the offset

between the local time zone and UTC. Here are a few examples:

VBScript - assuming IUSR has read access to registry! If this is not the case, you can use the same logic as

the VBScript example further on in this article.

<%
od = now()
set oShell = server.createobject("WScript.Shell")
atb = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\" &_
"Control\TimeZoneInformation\ActiveTimeBias"
offsetMin = oShell.RegRead(atb)
nd = dateadd("n", offsetMin, od)
Response.Write("Current = " & od & "<br>UTC = " & nd)
%>

JScript

<script language="JScript" runat=server>


var od = new Date();
var nd = od.toGMTString();
Response.Write('Current = ' + od + '<br>UTC = ' + nd);
</script>

Transact-SQL
SELECT GETDATE() AS CurrentTime, GETUTCDATE() AS UTCTime

Arbitrary Date / Time

Converting an arbitrary time is a little more involved. Because the registry only stores the CURRENT bias,

and doesn't keep historical record for previous dates, you may get invalid data if you are NOT in daylight

savings tiem and you are converting a date that is (or vice-versa). These examples will show how to find

GMT time for another date *in 2001* -- I will update this later to apply to any year. Note that for the

VBScript and T-SQL solutions, you should know your regular offset (these examples assume Eastern time

zone, which is 5 hours offset from UTC).

VBScript

<%
' fill in your known bias here!

offset = 5

' find first Sunday in April

for i = 1 to 7
if weekday("4/" & i & "/2001")=1 then
startDST = cdate("4/" & i & "/2001")
exit for
end if
next

' find last Sunday in October

for i = 31 to 25 step -1
if weekday("10/" & i & "/2001")=1 then
endDST = cdate("10/" & i & "/2001")
exit for
end if
next
' set some arbitrary date

od = "06/28/2001 4:35:08 AM"

' subtract hour from offset if within DST

if cdate(od) >= startDST and cdate(od) < endDST then


offset = offset - 1
end if

nd = dateadd("h", offset, od)

Response.Write("Current = " & od & "<Br>UTC = " & nd)


%>

JScript - a little smarter, JScript inherently knows when a date falls within DST, and adjusts accordingly.

<script language="JScript" runat=server>

// note that months are ZERO-based


// June 28 = 2001,5,28

var od = new Date(2001,5,28,4,35,08);


var nd = od.toGMTString();
Response.Write('Current = ' + od + '<br>UTC = ' + nd);
</script>

Transact-SQL
-- fill in your known bias here!

DECLARE @offset TINYINT


SET @offset = 5

DECLARE @dt CHAR(10), @sdt CHAR(10), @edt CHAR(10)


DECLARE @i TINYINT
SET @i = 1

-- find first Sunday in April

WHILE @i < 7
BEGIN
SET @dt = '04/0'+CAST(@i AS CHAR(1))+'/2001'
IF DATEPART(weekday,@dt)=1
BEGIN
SET @sdt = '04/0'+CAST(@i AS CHAR(1))+'/2001'
SET @i = 7
END
SET @i = @i + 1
END

-- find last Sunday in October

SET @i = 31
WHILE @i > 24
BEGIN
SET @dt = '10/'+CAST(@i AS CHAR(2))+'/2001'
IF DATEPART(weekday,@dt)=1
BEGIN
SET @edt = '10/'+CAST(@i AS CHAR(2))+'/2001'
SET @i = 24
END
SET @i = @i - 1
END

-- set some arbitrary date

DECLARE @od DATETIME


SET @od = '06/28/2001 04:35:08 AM'

-- subtract hour from offset if within DST

IF (@od>=@sdt AND @od<@edt)


SET @offset = @offset - 1

SELECT @od AS CurrentTime, DATEADD(hour, @offset, @od) AS UTCTime


How do I format a date in ways not offered by FormatDateTime?

1,443 requests - last updated Monday, July 22, 2002

Well, VBScript's FormatDateTime leaves a lot to be desired. There really are only two ways to format a date by

itself: VBLongDate and VBShortDate. Below are a few functions that will help you get dates into other relatively

common formats. Many of these functions use the following function, from Article #2300:

<%
Function pd(n, totalDigits)
if totalDigits > len(n) then
pd = String(totalDigits-len(n),"0") & n
else
pd = n
end if
End Function
%>

YYYYMMDD

(This is the preferred date format for passing dates to a database, as it eliminates the need to worry about regional

settings. See Article #2260 for more information.)

<%
response.write YEAR(Date()) & _
Pd(Month(date()),2) & _
Pd(DAY(date()),2)
%>

YYYY-MM-DD
<%
response.write YEAR(Date()) & _
"-" & Pd(Month(date()),2) & _
"-" & Pd(DAY(date()),2)
%>

DDMMYYYY

<%
response.write Pd(DAY(date()),2) & _
Pd(Month(date()),2) & _
YEAR(Date())
%>

MMDDYYYY

<%
response.write Pd(Month(date()),2) & _
Pd(DAY(date()),2) & _
YEAR(Date())
%>

DD-MM-YY

<%
response.write pd(DAY(date()),2) & "-" & _
pd(MONTH(date()),2) & "-" & _
pd(RIGHT(YEAR(date()),2),2)
%>

YY-MM-DD
<%
response.write pd(RIGHT(YEAR(date()),2),2) & "-" & _
pd(MONTH(date()),2) & "-" & _
pd(DAY(date()),2)
%>

MM/DD

<%
response.write pd(MONTH(date()),2) & "/" & _
pd(DAY(date()),2)
%>

Month D

<%
response.write MonthName(Month(date())) & _
" " & DAY(date())
%>

DD Mon YY

<%
response.write pd(DAY(date()),2) & " " & _
MonthName(Month(date()),true) & _
" " & RIGHT(YEAR(date()),2)
%>

Weekday, Month D
<%
response.write WeekdayName(Weekday(Date())) & _
", " & MonthName(Month(date())) & _
" " & DAY(date())
%>

Please let us know if there are any other date formats you'd like to see.
Should I use 'BETWEEN' for date queries?

1,243 requests - last updated Monday, April 15, 2002

NO, for three reasons:

(1) in my opinion, BETWEEN is ambiguous, at least in SQL Server. It is unclear to the casual observer whether

"...BETWEEN '20020104' AND '20020415' will include a record created at 4/15/2002 at 4:00 am; and I think people

EXPECT such a query to return results for all times on 4/15, not just those records that are entered at exactly

midnight. Much clearer to use >= '20020401' AND < '20020415'. This works better because all datetime values are

converted to midnight.

(2) for queries like 'give me all the records for this month', you tell me which is easier to construct:

WHERE dt BETWEEN '20020401 0:00:00' AND '20020430 23:59:59.999'

or

WHERE dt >= '20020401' AND dt < '20020501'

The latter is obviously easier to create, especially when doing so dynamically (you add a month to the beginning

date, instead of addig a month and then subtracting a second).

(3) when you are using other clauses in your query, you also have to remember to wrap the BETWEEN clause in

brackets so that its AND isn't confused with other ANDs in the WHERE clause. See Article #2148 for more

information on using OR and AND in a complex query.


How do I implement a calendar / datepicker in ASP?

1,175 requests - last updated Tuesday, July 2, 2002

Samples / code / articles:

javascript.internet.com/calendars/popup-date-picker.html

javascript.internet.com/calendars/window-calendar.html

www.kamath.com/calendar/

www.asp101.com/samples/calendar.asp

http://www.geocities.com/fuushikaden/PopupCalendar/sample.htm

www.dynamicdrive.com/dynamicindex6/dcalendar.htm

www.dynamicdrive.com/dynamicindex6/dcalendar2.htm

www.dynamicdrive.com/dynamicindex6/popcalendar.htm

developer.iplanet.com/viewsource/husted_calendar/husted_calendar.html

www.4guysfromrolla.com/webtech/060599-2.shtml

www.webreference.com/js/column64/13.html

msdn.microsoft.com/library/en-us/dnasp/html/calendar.asp

msdn.microsoft.com/workshop/author/behaviors/library/calendar/calendar.asp

www.clearviewdesign.com/NEWBIE/DemoPopup.asp

And here are some queries that returned lists of other code samples:
scriptsearch.internet.com/JavaScript/Scripts/Calendars/index.html

javascript.internet.com/calendars/index.html

www.aspin.com/func/search?qry=calendars

www.aspin.com/home/webapps/calendars

www.developersdex.com/asp/search.asp?Search=calendar
How do I determine the number of seconds since 1/1/1970?

1,061 requests - last updated Monday, November 12, 2001

This is a pretty common one, as many systems use this measure for various date calculations. Here are examples in

VBScript, JScript and Transact-SQL. Note that to get the number of milliseconds since 1/1/1970, you need to

multiply these results by 1000.

VBScript

<%
timeStart = "1/1/1970 12:00:00 AM"
Response.Write(datediff("s", timeStart, now()))
%>

JScript - we need to correct for UTC, which is already accounted for.

<script language='jscript' runat=server>


var timeStart = new Date();
var tz = timeStart.getTimezoneOffset()*60*1000;
timeStart = Math.floor(((timeStart.getTime() - tz))/1000);
Response.Write(timeStart);
</script>

Transact-SQL

SELECT DATEDIFF(SECOND, '1/1/1970 12:00:00 AM', GETDATE())

Note also that these solutions do not account for leap seconds. You can add them in manually for leap seconds that

have been known to occur in the past (06/30/1997 and 12/31/1998 are two examples). You can not predict them

programmatically... since leap seconds are defined by the variability of the Earth's rotation, and are declared by the

International Earth Rotation Service as required. See this Google posting and this U.S. Navy article for highly

detailed information about leap seconds and how they affect UTC. After reading these, you may see why your

calculations don't need to be this precise -- especially since your web and/or SQL Server are probably not set to
atomic clocks anyway. :-)
How do I convert a timespan, in seconds, to HH:MM:SS?

686 requests - last updated Wednesday, March 27, 2002

Often you want to compare two dates and/or times and express the difference in HH:MM:SS. Except there is no

decent way to express the relationship this way; the built-in DATEDIFF function returns a single integer (seconds,

minutes, hours, days, months, years, etc).

Here is a way in T-SQL to accomplish this:

DECLARE @dt1 DATETIME, @dt2 DATETIME


SET @dt1 = '2002-03-27 09:20:25'
SET @dt2 = '2002-03-27 09:20:45'

DECLARE @seconds INT, @minutes INT, @hours INT


DECLARE @secStr VARCHAR(2), @minStr VARCHAR(2), @hoursStr VARCHAR(4)
DECLARE @diff CHAR(10)

SET @seconds = DATEDIFF(SECOND, @dt1, @dt2)

SET @minutes = @seconds / 60


SET @hours = @minutes / 60

SET @minutes = @minutes % 60


SET @seconds = @seconds % 60

SET @secStr = CAST(@seconds AS VARCHAR(2))


SET @minStr = CAST(@minutes AS VARCHAR(2))
SET @hoursStr = CAST(@hours AS VARCHAR(2))

SET @diff = LEFT('00',2-LEN(@hoursStr))+@hoursStr + ':'


+ LEFT('00',2-LEN(@minStr))+@minStr + ':'
+ LEFT('00',2-LEN(@secStr))+@secStr

SELECT diff = @diff

Here it is in VBScript:
<%
dt1 = "2002-03-27 9:20:25 AM"
dt2 = "2002-03-27 9:20:45 AM"

seconds = datediff("s", dt1, dt2)


minutes = seconds \ 60
hours = minutes \ 60
minutes = minutes mod 60
seconds = seconds mod 60

response.write left("00",2-len(hours)) & hours & ":"


response.write left("00",2-len(minutes)) & minutes & ":"
response.write left("00",2-len(seconds)) & seconds
%>

JScript example forthcoming.


Why do I have problems inserting NOW() into a database?

607 requests - last updated Saturday, August 10, 2002

Several hiccups can happen when trying to INSERT or UPDATE a record with the current date and time. For one,

regional settings and/or SQL Server's dateformat setting can throw you for a loop (see Article #2260 for some

workarounds to regional settings issues), and even delimiters can get in the way when moving between platforms

(see Article #2023).

My suggestion: let the database put the date and time on the record. In SQL Server, you can do this:

CREATE TABLE blah


(
foo VARCHAR(5),
bar DATETIME NOT NULL DEFAULT GETDATE()
)

(And for UPDATEs, you could create a trigger.)

This way, you don't have to worry about your application code getting the date format correct (since SQL Server will

automatically generate the current time for you, and store it in its own internal format), and you can have one less

column in your INSERT list.


How do I prevent my ASP pages from caching?

30,742 requests - last updated Thursday, November 7, 2002

At the top of the page:

<%
Response.ExpiresAbsolute = #2000-01-01#
Response.AddHeader "pragma", "no-cache"
Response.AddHeader "cache-control", "private, no-cache, must-revalidate"
%>

(Thanks to Ken Schaefer for suggesting a more compact version.)

If you find that you're still getting the old page, after clearing your browser's page and even deleting the file from

the server, then IIS has it cached. You can clear this by going into the IIS Admin interface and unchecking "Cache

ISAPI Applications", hitting apply, uploading the new file, and turning the setting back on. You can also do this by

issuing an iisreset call or unloading your application, at the cost of interrupted service.
How do I execute a DOS command / batch file / exe from ASP?

28,100 requests - last updated Tuesday, August 27, 2002

If you have Windows Script Host installed and enabled, you can do this (sorry, I've only had the time to test .bat

files):

<%
set wshell = server.createobject("wscript.shell")
wshell.run "c:\file.bat"
set wshell = nothing
%>

Where c:\file.bat is something like:

net stop iisadmin /y


net start w3svc

If you do not have the ability to use WSH, you can use ASPExec or DynuExec.

If you are trying to retrieve output from a command, see the example in Article #2033, which captures the results of

a PING command. Another alternative is to pipe the output to a text file, which you can then read using the

FileSystemObject; for example:

<%
set wshell = server.createobject("wscript.shell")
wshell.run "dir c:\ > c:\dir.txt"
set wshell = nothing

set fso = server.createobject("Scripting.FileSystemObject")


set fs = fso.openTextFile("c:\dir.txt",1,true)
response.write replace(replace(fs.readall,"<","<"),vbCrLf,"<br>")
fs.close: set fs = nothing: set fso = nothing
%>
If you receive the following error:

Error Type:
(0x80070002)
/<file>.asp, line <line>

This usually means that the file is not found. Check the name and path of the file you passed to the run method.

If you receive the following error:

Microsoft VBScript runtime error '800a0046'


Permission denied
/<file>.asp, line <line>

Then either one of two things happened. Either the interactive user (whether IUSR_MachineName or an

authenticated user) does not have permissions to the file or folder being executed; or, the interactive user does not

have permissions to one or more of the commands being called within the file.
How do I read the contents of a remote web page?

24,907 requests - last updated Saturday, September 21, 2002

You can include static txt and HTML files from remote servers by using a component (such as AspHTTP, ASPTear, or

VB's built in InetCtrls) to parse the remote URL's content.

You can also try this method out; it was tested with the MSXML objects which are installed with Windows 2000. For

performance reasons, and just to have "the latest", you should download the new version 4.0 for Windows 2000 or

other operating systems. If you download the newer version, take special note of the new ProgID you should be

using -- MSXML 4.0 now supports side-by-side installation, which means the ProgID below will actually use the older

version.

<%
url = "http://www.espn.com/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
xmlhttp.open "GET", url, false
xmlhttp.send ""
Response.write xmlhttp.responseText
set xmlhttp = nothing
%>

And here it is in JavaScript:

<script language=javascript runat=server>


var url = "http://www.espn.com/main.html";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP");
xmlhttp.open("GET", url, 0);
xmlhttp.send("");
Response.Write(xmlhttp.responseText);
var xmlhttp = null;
</script>

If you use a URL that doesn't exist, or you are behind a firewall that blocks certain web sites, or the site is behind a

firewall that blocks traffic to port 80 / 443, or you are using a proxy server, or the site requires authentication, you
will receive this error:

msxml4.dll (0x80072EE7)
Server name or address could not be resolved

To correct, you will have to figure out which of the issue(s) is standing in your way, and discuss workarounds with

your or their network administrator(s).

Don't forget that if your remote page has relative image URLs, or style sheets, or JavaScript files, or frames, or

links, it won't work perfectly when ported to your server(s). To overcome this, you'll want to add a BASE HREF tag

to keep all the images coming from the correct location. For example, the above code (which gets all the text from

espn.com, but is formatted weird and doesn't function 100% as intended), is modified only slightly to work

correctly:

<%
url = "http://www.espn.com/main.html"

' add a BASE HREF tag


Response.write "<BASE HREF='" & url & "'>"

set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")


xmlhttp.open "GET", url, false
xmlhttp.send ""
Response.write xmlhttp.responseText
set xmlhttp = nothing
%>

For information on increasing or decreasing the time allowed for the XMLHTTP objects to retrieve a response from a

remote server, see Article #2407.

If you need to POST data you can so by adding a header that tells the receiver you're sending FORM data:
<%
url = "http://www.espn.com/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
xmlhttp.open "POST", url, false
xmlhttp.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
xmlhttp.send "x=1&y=2"
Response.write xmlhttp.responseText
set xmlhttp = nothing
%>

Another thing you may want to do, going back to the original script, is make sure the server is there! If not, you can

display a message...

<%
' deliberate typo:
url = "http://www.espn.co/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
on error resume next
xmlhttp.open "GET", url, false
xmlhttp.send ""
if err.number <> 0 then
response.write "Url not found"
else
Response.write xmlhttp.responseText
end if
set xmlhttp = nothing
%>

You might want to parse the results, instead of sending them straight to the client:
<%
url = "http://www.espn.com/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
on error resume next
xmlhttp.open "GET", url, false
xmlhttp.send ""
if err.number <> 0 then
response.write "Url not found"
else
if instr(xmlhttp.responseText,"Stanley Cup")>0 then
response.write "There's a story about the playoffs."
response.write "<a href=" & url & ">Go there</a>?"
else
response.write "There is no story about the playoffs."
end if
end if
set xmlhttp = nothing
%>

You may be interested in performing an asynchronous request, e.g. hitting an ASP page that acts like a batch file

that gets fired but does not need to return any results. You can simply change the third parameter of the open call

to TRUE (and leave out the reference to the responseText value):

<%
url = "http://www.espn.com/main.html"
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
xmlhttp.open "GET", url, true
xmlhttp.send ""
set xmlhttp = nothing
%>

Finally, you may want to spoof your user agent, since the MSXML object sends something like "Mozilla/4.0

(compatible; Win32; WinHttp.WinHttpRequest.5)" -- many sites will view this as a spider or 'screen scraper', and for

various reasons, might present alternate content -- here are two samples:
<%
url = "http://www.espn.com/main.html"

' this sample posts as the actual browser being used:

br = request.servervariables("HTTP_USER_AGENT")
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
on error resume next
xmlhttp.open "GET", url, false
xmlhttp.setRequestHeader "User-Agent",br
xmlhttp.send ""
if err.number <> 0 then
response.write "Url not found"
else
response.write xmlhttp.responseText
end if
set xmlhttp = nothing

' this sample posts as "My funky browser."

set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")


on error resume next
xmlhttp.open "GET", url, false
xmlhttp.setRequestHeader "User-Agent","My funky browser."
xmlhttp.send ""
if err.number <> 0 then
response.write "Url not found"
else
response.write xmlhttp.responseText
end if
set xmlhttp = nothing
%>
If you encounter errors... you can use ParseError to determine the problem.

<%
set xmlhttp = server.CreateObject("MSXML2.ServerXMLHTTP")
' ... stuff ...
on error resume next
xmlhttp.send ""
if err.number <> 0 then
response.write "Error: " & xmlhttp.parseError.URL & _
"<br>" & xmlhttp.parseError.Reason
response.end
end if
' ... stuff ...
%>
What is this 'ASP 0115' error?

20,599 requests - last updated Monday, September 16, 2002

I think we've all seen this error:

error 'ASP 0115'


Unexpected error
A trappable error occurred in an external object. The script cannot
continue running.

or

error 'ASP 0190'


Unexpected error
A trappable error occurred while releasing an external object.

or

error 'ASP 0191'


Unexpected error
A trappable error occurred in the OnStartPage method of an external object.

or

error 'ASP 0192'


Unexpected error
A trappable error occurred in the OnEndPage method of an external object.

For quick symptom relief, simply restart IIS using the batch file referenced in Article #2087

The ASP 0115 error is IIS' way of saying "there was an error, but I don't know the cause." This is because the error

came from something external to ASP (e.g. a custom COM object or an Oracle database).

Listed below are some of the common causes for ASP returning the 0115 error, followed by some recommended

troubleshooting techniques:
● Permission and authentication issues with files and registry keys. (May also produce 80040150 errors (Could

not read key from registry).

Errors may occur if the authenticated user does not have sufficient permissions on other files such as custom

components, system dynamic-link libraries (DLLs), and even registry keys.

ASP scripts are typically executed in the security context of the IUSR_<machine_name> account.

If you believe you are dealing with a permissions problem in the registry, you can use Regedt32.exe to

examine permissions on the various registry keys. In particular, you may want to look at ODBC, Jet, ADO,

and other keys that might be relevant to the problem. If you have a machine that is working properly, try

comparing key permissions between the two machines.

The first step is to determine if you really are seeing a permissions problem. A good test is to temporarily

add the anonymous logon account (IUSR_<machine_name>) to the administrators group using User

Manager. This gives the IUSR_<machine_name> account administrative privileges on the machine. If this

causes ASP to function properly, you are almost certainly dealing with a permissions issue.

Note: When you have finished debugging, be sure to remove the IUSR_<machine_name> account from the

administrators group to minimize the security risk on your server.

Refer to the following article for additional information:

Q185874 How to Troubleshoot Permissions in IIS 4.0

● Incorrectly registered dlls or incompatible dll version issues.

If you are developing COM objects with Visual Basic, you might create a dependency file and compare the

file verions to the files installed on your server.

Refer to the following articles:

Q178354 HOWTO: Setup Wizard Dependency Files

Q185599 BUG: OleAut32.dll Is Unregistered Incorrectly


Q145676 Registry Overwritten with Same ProgID/Different Bitness Server

● Use of ScriptingContext with OnStartPage / OnEndPage - use ObjectContext method instead. See this

tutorial for a simple VB6 ActiveX DLL that uses ObjectContext as opposed to ScriptingContext.

● Unhandled errors returned from components.

● Form elements are not named. Refer to the following article:

Q173741 PRB: Random ASP 0115 Errors when Submitting Form

● Use of the ASP Session Object prior to version 1.24.09 of the ASP dll

Refer to the following article:

Q177036 FIX: ASP 0115 Error Occur With The Session Object

● Connecting to Oracle with an old version of msorcl32.dll

Get a new version with the latest version of MDAC from http://www.microsoft.com/data/.

FWIW, my current machine has version # 2.573.7924.0.

If the connection alone is fine, try a firehose recordset.

Oracle through OLE-DB doesn't support some advanced rs properties.

● Use of components that are not thread-safe in a multi-threaded environment.

Refer to the following articles:

Q191979 PRB: VB Component Not Marked Apartment Produces ASP 0115 Error

Q172925 INFO: Security Issues with Objects in ASP and ISAPI Extensions

Q150777 INFO: Descriptions and Workings of OLE Threading Models

● Using Microsoft Data Access Components (MDAC) 2.0 on IIS 3.0.


Refer to the following article:

Q193310 FIX: ADO 2.0 Generates Error 0115 When Used with IIS 3.0

Other potential fixes:

- ensure Stored Procedure parameters without have correct data types or lengths

- ensure Stored Procedures don't use reserved words

- toggling the memory space / protection of an Application

- updating to the latest version of MDAC (http://www.microsoft.com/data/)

- handling errors correctly inside custom VB or VC++ components

Finally, check out this KB article:

Q194190 PRB: Error "ASP 0115 a Trappable Error Has Occurred"


Where else can I learn about ASP?

20,535 requests - last updated Friday, July 12, 2002

If you're looking for info on ASP+, Visual Studio 7, and .NET technologies, look here:

Where can I find out about ASP+ (ASP Plus), Visual Studio 7, and .NET?

For "regular" ASP, there are many sites out there. A beginner's tutorial has been published by Microsoft:

Active Server Pages Tutorial

The following list is not exhaustive. All sites are free except for one exception:

Action Jackson

Active Server Corner

ASP-help.com

ASP 101

ASP Free

ASP Hole

ASP Key

ASP Resource Index

ASP Today ($$$ Annual Fee $$$)

DeveloperFusion.com

Developersdex
DevGuru

4guysfromrolla

15 Seconds

IIS Answers

IIS FAQ

Infinite Monkeys

LearnASP.Com

MSDN Online

Planet Source Code

powerASP.com

Stardeveloper.com

SWYNK

T-Cubed

Ultimate ASP

VisualBasicScript.com

VisualBuilder.com

www.serverscripting.com

aspfaqs.com
Microsoft has created a list of ASP Knowledge Base articles. While they didn't have enough foresight to make them

LINKABLE, at least you can scan the titles with relative ease.

INFO: ASP Knowledge Base Article Index


How do I disable the back/forward buttons?

19,848 requests - last updated Tuesday, July 11, 2000

Short of disabling the entire toolbar, you can't. Even then, people can use Backspace or Alt+left. Instead of trying to

disable the features of a browser, build your application so that disabling those features is not necessary. For

example, if your application inserts a record into a table, and you're afraid that if the user clicks back it will insert

another one, there are at least two things you can do (and likely several others): use a session variable to track the

insert, or prevent duplicate values on certain fields.

Web Men Talking recently had a decent article describing the various ways to deal with this behavior.
How do I control printing from ASP?

19,327 requests - last updated Tuesday, August 27, 2002

Printing happens on the CLIENT. Therefore, you can only control anything to do with printing, from the client... and

not from ASP. Changing margins, header & footer, hiding some things on your page from printing, forcing page

breaks, and even initiating the printer at all are tasks that can only be accomplished from the CLIENT side. Please

keep in mind that you can't (and shouldn't want to) FORCE the user to print your page, or do so without telling them

(i.e. allowing them to confirm) that you're about to send data to your printer.

This article does a good job of exposing print templates (assuming clients of IE 5.5+):

http://www.webreference.com/js/column89/

Mead Co. provides an effective solution which adds the ability to control many of IE's printing features from

scripting, such as headers/footers/margins/landscape:

http://www.meadroid.com/scriptx/index.htm

HTMLPrinting.com also has a product that allows you to control printer settings:

http://www.htmlprinting.com/

Heidi Housten demonstrates a way to invoke the Print dialog:

http://members.tripod.com/~housten/printing.html

You can hide things from printing by using a different style sheet setting for screen and print media types:

http://www.w3.org/TR/REC-CSS2/media.html

And you can force a page break at certain points in your document:

http://www.w3.org/TR/REC-CSS2/page.html

A couple of other useful printing techniques can be found here:


http://msdn.microsoft.com/workshop/author/script/dhtmlprint.asp

Now whether the 'other' browser is up to all these tasks is beyond me... and isn't really on-topic here. This is an ASP

site, after all. :-)


How do I get screen resolution from ASP?

19,109 requests - last updated Wednesday, March 6, 2002

Screen Resolution has always been a tough egg to fry. In most browsers, you can't get a screen resolution. But if

you can, what do you plan to do with it? If I have a screen resolution of 1600x1200, it doesn't mean that my

browser is that size. So if you create a table that is 1550 pixels wide, just because you've detected my resolution, if

my browser is not maximized I'm going to have to scroll all over the place to see the whole page.

In IE 4 and Netscape 4, you can detect a much more meaningful variable, the width/height of the browser window.

For this, you need client-side script.

*****

IE 4+:

<script language="JavaScript">
var w = document.body.clientWidth;
if (w>=650)
{
window.location.href="bigscreen.htm";
}
else
{
window.location.href="smallscreen.htm";
}
</script>

*****

NN 4:
<script language="JavaScript">
var w = window.innerWidth;
if (w>=650)
{
window.location.href="bigscreen.htm";
}
else
{
window.location.href="smallscreen.htm";
}
</script>

Of course this leaves out the 3.0 browsers. Here is how to handle this in IE 3.0, which provides the SCREEN size

(note, not the window size).

*****

IE 3:

<%
smallurl = "smallscreen.htm"
bigurl = "bigscreen.htm"
a = request("http_ua_pixels")
url = smallurl
if instr(a,"x")>0 then
a = split(a,"x")
if clng(a(0)) >= 650 then
url = bigurl
end if
end if
response.redirect(url)
%>

And NN 3.0 has to be different... in that case, to get the screen resolution (again, not the more meaningful browser

window size), you need to hook up Java.

*****
NN 3:

<script language="JavaScript">
var Sizer=java.awt.Toolkit.getDefaultToolkit()
var ScrSize=Sizer.getScreenSize();
var ScrW=ScrSize.width;
if (ScrW >=650)
{
window.location.href="bigscreen.htm";
}
else
{
window.location.href="smallscreen.htm";
}
</script>

*****

So, the ASP solution (which, admittedly, only takes into account the two major browsers; Opera and others will need

special considerations):

<%
bigurl = "bigscreen.htm"
smallurl = "smallscreen.htm"
A = LCase(Request.ServerVariables("HTTP_USER_AGENT"))
if instr(A,"msie 5")>0 or instr(A,"msie 4")>0 then
%>

<script language="JavaScript">
var w = document.body.clientWidth;
if (w>=650)
{
window.location.href="<%=bigurl%>";
}
else
{
window.location.href="<%=smallurl%>";
}
</script>

<%
elseif instr(A,"msie 3")>0 then

smallurl = "smallscreen.htm"
bigurl = "bigscreen.htm"
a = request("http_ua_pixels")
url = smallurl
if instr(a,"x")>0 then
a = split(a,"x")
if clng(a(0)) >= 650 then
url = bigurl
end if
end if
response.redirect(url)

elseif instr(A,"zilla/4")>0 Then


%>

<script language="JavaScript">
var w = document.body.clientWidth;
if (w>=650)
{
window.location.href="<%=bigurl%>";
}
else
{
window.location.href="<%=smallurl%>";
}
</script>

<%
elseif instr(A,"zilla/3")>0 then
%>

<script language="javascript">
var Sizer=java.awt.Toolkit.getDefaultToolkit();
var ScrSize=Sizer.getScreenSize();
var ScrW=ScrSize.width;
if (ScrW>=650)
{
window.location.href="<%=bigurl%>";
}
else
{
window.location.href="<%=smallurl%>";
}
</script>

<%
else
respose.redirect(smallurl)
end if
%>

This is a lot of manual work. BrowserHawk will do this for you automatically, taking all browsers into effect.
How do I schedule ASP files?

18,458 requests - last updated Monday, September 9, 2002

1. Use the AT command and Windows Scripting Host (or the more rudimentary task scheduler) to schedule a

VBS file at certain intervals.

First, change the ASP to a VBS file. This is accomplished by (1) changing the extension to VBS; (2) changing

all server.createobject calls to createobject; and, (3) removing all <%%> delimiters and any browser-

destined code (for example, response.write statement or client-side HTML). I didn't run into any further

complications, but YMMV.

You store the VBS file in the filesystem, and use the AT command to schedule it (this actually schedules its

execution with NT's schedule service). At a command prompt, you can use AT by itself to see a list of tasks

currently in the schedule. You can use AT /? to find out all its syntax possibilities.

For example, to get a file to run every weekday at 9:00 am, I launch this batch file (the first line clears

existing entries):

at /delete /y
at 9:00 /every:m,t,w,th,f d:\net\shared\getdata.vbs

Notice there is no web server involved; the file is accessed directly through the file system. Once I got over

the "a user has to be logged in" and "the tasks have to be reset when rebooted" hurdles (both of which I

believe are problems with the particular machine that is not under our control), all has been running fine for

me.

For an example of using WSH, CDONTS and the Task Scheduler to send out e-mails on a regular basis, see

Q221495.

2. If all you are doing is database work in SQL Server, you might consider using a job. This will allow you to

keep all the processing of the job within your database, and prevent the complications associated with

multiple systems, connections, and adapting ASP code to be non-ASP-like in behavior. See Article #2403 for

a quick tutorial on configuring a job within SQL Server.


3. Kris Eiben suggests: If it's a high-traffic site, you might also be able to put something in session_OnStart,

using an application variable with the last time the function was run and checking to see if the right amount

of time has passed.

Along the same lines, check out this article at powerasp.com - it shows you how to use global.asa, a text file

and datediff to schedule execution... though timing is never exact because it relies on visitors actually hitting

your site.

4. Build an ASP page and leave a browser open on the machine, with a <meta> refresh (this is by far the

ultimate kludge).
Why do I get a 500 Internal Server error for all ASP errors?

18,117 requests - last updated Monday, August 12, 2002

If you're using IE5 and/or developing on Windows 2000, you might find it difficult to debug ASP errors in a browser.

This is because IE5 has a ridiculous default option that suppresses errors to a more "friendly" error (which, IMHO, is

a lot more cryptic than what they'd get otherwise). This comes back to the user as a 500.100 Internal Server Error

(ASP 0147), and doesn't leave the user much information to pass on to the webmaster, except to tell them that "The

page cannot be displayed."

To circumvent this silliness and get real ASP errors, go to IE's Tools/Internet Options menu, and on the advanced

tab, uncheck "Show friendly HTTP error messages."

After you've disabled this default setting, refresh the page in question. There are four possible outcomes: (1) the

page will magically work again; (2) the page will give you a more detailed error (e.g. Stack Overflow or Syntax

Error), including a line number; (3) you will get "Server Application Error" - which means that at some point IIS got

confused about the current application; or (4) you will still see non-descript error messages.

If (4) is what happens, open Internet Services Manager, go to the home directory tab of your default web site or

application, click on configuration, go to the Debugging tab, and make sure "Send detailed error messages to the

client" is selected. Click Apply/OK/OK etc. to get out of there and try your page again.

If (3) is what happens, you can remedy this simply by going into Internet Services Manager. Right-click the

application in question (or Default Web Site, if an application is not relevant), select properties, hit the "Home

Directory" tab, click the "Remove" button, and then click the "Create" button. Follow with Apply/OK etc and get out

of Internet Services Manager. Refresh your page, and all should be well again.

If you are still getting errors like 'page not found' then go into Internet Services Manager, right-click Default Web

Site, choose Properties, and on the Home Directory tab, click the Configuration button. On the App Debugging tab,

make sure "Send detailed ASP error messages to client" is selected.

Finally, check the event log, as occasionally there is more information there than you will see in the browser. Of

course this is even more true if you can't change the IIS or browser configurations.

See Q261200 and Q311766 for more information.


What is wrong with Session_OnEnd()?

16,505 requests - last updated Sunday, October 28, 2001

1. Why does Session_OnEnd not fire?

Session_OnEnd is unreliable. Do not create applications that rely on Session_OnEnd to occur, because it

doesn't always happen. This is a known bug and seems to have been reduced (if not eliminated entirely) in

IIS 5.0, which ships with Windows 2000. This bug is one of many reasons to not store recordsets,

connections or other objects in session variables (see Article # 2053).

So is there any way to get around this? Well, let's say you have an application that stores session data in a

database. When the session ends, you want to clean up that data, right? If the user logs out properly, you

can do this from session_onEnd. If leave your site in any other way, the session data will be stored forever.

For this example, let's say your session timeout is the default, 20 minutes.

On every page, you should update the session record(s) to reflect the CURRENT time for that user. This way,

the record will become stale if there's no activity... and if they don't log out, you still have some way of

identifying that their session has, indeed, expired (whether that window is still open, or they've gone to

xxx.com, or they've closed their browser, or whatever).

Then you could create a stored procedure that runs every 20 minutes (or the value of session timeout, or

every hour, or once a day) by scheduling a job through SQL Server Agent. This stored procedure would scan

the table and set inactive (or delete) any records older then <x> minutes. You could also use it to delete

any temporary files or folders that the user was using on the server during their session.

2. Why can't I use server.mapPath or response.redirect?

Session_OnEnd does not support the request, response or server objects. The only built-in objects you can

use are session and application. So, for example, if you need to use a server.mapPath directive, store the

fully qualified path in an application variable BEFORE the session ends. However, in limited testing, I was

able to use these objects in files #included in global.asa (for the technique, see Article #2144).
How do I use ASP to [...]

15,501 requests - last updated Friday, October 13, 2000

...access a user's favorites list, make my site their home page, delete their cache, delete their history,

remove one item from their cache, remove one item from their history, write to a text file on their hard

drive, figure out their home page, determine their custom settings, put a shortcut to my home page on

their start menu, force them to download a file without a prompt, change their default download folder,

adjust their page margins for printing, automatically print a page without a prompt, change their default

printer, disable the edit button, disable view source, disable the back/forward buttons, change their

default browser/e-mail client/newsreader, "borrow" their e-mail address, read a key from their

registry, change their security settings, force them to save my web page to their hard disk, change their

"browse in a new process" setting, change their screen size/resolution/color depth, force them to

download an ActiveX control/plugin, automatically run an EXE on the client, force them to enable

cookies, grab or delete files from their machine without asking, or force them to view my Java applets

even though they have disabled Java?

A. You don't. At least not from ASP (and preferably not from anywhere else either!). There are very good

reasons why each of these things is either extremely difficult (usually requiring an ActiveX control) or

impossible. To get a better sense of this, imagine how you would feel if the aspfaq.com site (or any other

site, for that matter) did any of these things to YOUR computer.
How do I control access to an area?

14,385 requests - last updated Tuesday, November 13, 2001

Check out these articles:

Implementing a secure site with ASP

IIS Security Overview

If you want to roll your own permissions, then creating a login for a section of your web site is fairly easy. First,

create a login form (loginForm.asp):

<form action=loginHandler.asp method=post>


Username: <input type=text name='username'><BR>
Password: <input type=password name='password'><BR>
<input type=submit Value='Log In'><BR>
</form>

Next, create a login handler (loginHandler.asp):

<%
u = lcase(request.form("username"))
p = lcase(request.form("password"))

'---------------------------------------------------------
'-- check to see that the form was completely filled out--
'---------------------------------------------------------
if u="" or p="" then
response.redirect("loginForm.asp")
end if

'---------------------------------------------------------
'-- check for a match, this could be against a database!--
'---------------------------------------------------------
if u<>"myusername" or p<>"mypassword" then
'access denied
response.redirect ("loginForm.asp")
else

' let them in!


session("login")=true
response.redirect ("hiThere.asp")
end if
%>

Finally, at the top of each page, you test the session variable that you assigned in the script above:

<%
if not session("login") then
response.redirect("loginForm.asp")
end if
%>

You could also do something similar using a database, where you would only have to check that the username and

password exist in a table of many u/p combinations (in the above example, you have to manually program each

username/password you want to support).


How do I make sure my ASP question gets answered?

13,906 requests - last updated Saturday, January 13, 2001

Posting to a newsgroup requires a little bit of netiquette.

1. Make sure your system date and time are valid. If you post in the future, your question will rarely be

answered with anything but sarcasm.

2. Please be specific. Don't say that a page you have "does not work" or is "broke." Define what "does not

work" means. Display your code, particularly the line that is causing the error (along with the exact error

message). If you are getting database errors, copy the error verbatim *and* include the resulting SQL string

that is being attempted (most errors that are posted to the groups would have been caught long before that,

had the user replaced conn.execute(sql) with response.write(sql)). Specify which version of everything

you're running: IIS 2, 3, 4 or 5; PWS 1.x or 4.0; which operating system

(W95/W98/WME/NTWks/NTServer/W2KPro/W2KServer/XPPro/XPHome); which database platform you're

connecting to (and version); and which version of MDAC you are using. The more information people have,

the better they'll be able to help you.

3. Please do not cross-post to all 3 groups just because they're about ASP. Someone will answer your question

if you post it in the right group. If you put a blanket question out across all 3 groups in an effort to get more

response, many people (myself included) will be much less eager to answer your question. This is mainly

because the experts in the database group are not likely able to answer your questions about CDONTS, or at

least that's not their specialty. So to them, your post is just noise as opposed to signal. Also, many server

issues will likely be better handled in the group microsoft.public.inetserver.iis.

4. CHECK THIS FAQ FIRST. If you ask a question that's already answered in the FAQ, at least one of the

answers you get will be a link here. Again, this simply wastes time and bandwidth. Please look over the

recent posts in the newsgroup. 80% of new posts are ones that were asked and answered within the past 2

days, and too often within the past 2 hours. Doing a quick scan of the topics in the newsgroup might save

you from posting your question at all, let alone waiting for a response (which, in this case, would often just

be directions to scrolling down to a specific post anyway). Along these lines, Google is an outstanding

resource; I recommend you all try out its advanced search feature. Instead of waiting for answers from

people who may be listening in this group today and have time to post an answer, you can enter your search

criteria, time frame, specific groups (e.g. '*asp* or *iis*') and you can even include (or exclude) a poster's

name from the result set. Their database is humungous, encompassing all posts made to these groups at
least...

5. Please provide a relevant and meaningful subject line. "Help! It's broken." or "I have an ASP problem" are

not very helpful subject lines. To make it easier for people to help you, put something more specific there,

like "SQL Error: Error in update statement" or "DeleteFile() error: object doesn't support this property or

method."

6. Please do not lash out unreasonably at people trying to help you, just because they didn't provide the exact

answer you were looking for, or pointed you to sufficient documentation instead of dropping everything to

write your entire application for you. Keep in mind that these people are volunteers and they are helping you

of their own free will. Mistreat them, and they'll stop helping you. If you don't like an answer they give,

move on to the next one... and if you absolutely *must* call names, do it in private mail.

For offical newsgroup rules, see Microsoft's Rules of Conduct...


How do I make JavaScript send values to ASP?

13,188 requests - last updated Friday, June 14, 2002

This question is asked a LOT. "How do I set a session variable equal to something that was just created in client-side

script?" You CAN'T. Because ASP is processed on the server, and JavaScript isn't processed until AFTER the results of

ASP have been passed to the client, the only way to send JavaScript variables to ASP is to invoke another ASP page

(e.g. submit a form, client-side redirect, auto-post to a hidden frame, etc.). Makes sense too, since you can't USE

the new session variable until you load another ASP page anyway.

Having said all that, there are workarounds. Several examples are given in Article #2281
How do I get the user's IP address or browser information?

12,572 requests - last updated Tuesday, July 30, 2002

The user offers up many environment variables without even knowing it. You can learn a LOT about these variables

by running the following script:

<table>
<%
for each x in Request.ServerVariables
Response.Write("<tr><td>" & x & "</td><td>")
Response.Write(Request.ServerVariables(x))
Response.Write("</td></tr>" & vbCrLf)
next
%>
</table>

Here is a JScript equivalent:

<table>
<script language='JScript' runat=server>
var svColl = Request.ServerVariables();
for(sv = new Enumerator(svColl); !sv.atEnd(); sv.moveNext())
{
var nm = sv.item();
Response.Write("<tr><td>" + nm + "</td><td>");
Response.Write(svColl(nm) + "</td></tr>");
}
</script>
</table>

Note that in the case of the user's IP address, which is stored in the REMOTE_ADDR variable, this value isn't reliable.

Anyone behind a proxy / firewall will reflect the IP address of the proxy rather than their own machine, so people at

big companies can all appear to be the same user. This case is even more significant with AOL, which has a very

limited set of IP addresses representing their entire user base of millions. The best form of unique idenitification is

going to involve sessions (short-term) or cookies (repeat visits), if the user accepts cookies. Over the long term, you
can store a GUID, IDENTITY value, or something else that will uniquely identify this user in the future, in a database.

Keep in mind that the user can, at any time, delete their cookies. So if their information is important, you might also

provide some way for them to re-establish their cookie through a login mechanism.
How do I protect my ASP code?

11,942 requests - last updated Tuesday, June 26, 2001

As we all know, ASP code is relatively safe from surfers. When people try to download ASP code, all they see is the

processed HTML (unless you haven't patched the ::$DATA bug).

Many people are concerned, however, about protecting their ASP code from prying eyes (either code they are

distributing to clients, or code that sits on a shared server).

You can compile your code into DLLs. Often this isn't an option, because most hosts won't accept custom

components (some, like Data Return, will accept it -- if you turn over administrative control and allow them to

review your source code). Read this article for a tutorial on creating a simple VB6 ActiveX DLL.

The other option is to use Windows Script Encoder. However, before you decide to go this route, make sure you read

this article, which demonstrates how easy it is to reverse engineer these 'encoded' scripts.
Where do I get ASP?

11,924 requests - last updated Tuesday, October 30, 2001

=====================

Windows 95 / NT 4.0 Workstation

=====================

The latest version of Personal Web Server / IIS is 4.0. It includes ASP 2.0, and is found in the Windows NT 4.0

Option Pack, which you can download here. If you are running NT 4.0 and IIS 3.0 / Peer Web Services, these

products do NOT include ASP. You will have to find ASP.exe to make ASP work; however, I'm not going to place the

link here because I strongly believe that you should use version 4.0.

After installing the Option Pack, you'll want to [re-]install Service Pack 6a (which, among other things, updates

ASP.dll and fixes several security holes in IIS), and also keep your MDAC components up to date by watching the

MDAC download page for new releases.

=====================

Windows 98

=====================

Find "pws.exe" on the Windows 98 CD. THAT is the version you should be installing, NOT the one from the web site

and NOT the one from the FrontPage CD. After installing PWS, keep your MDAC components up to date by watching

the MDAC download page for new releases.

=====================

Windows 2000 Professional

=====================

Windows 2000 includes a scaled-down version of IIS 5.0. If you didn't install IIS during initial installation, you can

find it under Control Panel >> Add/Remove Programs >> Add/Remove Windows Components.

=====================

Windows Millennium / XP Home

=====================
Article #2079 contains a description of Microsoft's lack of support for these configurations, as well as workarounds to

make them work correctly.

=====================

Windows XP Professional

=====================

Windows XP includes a scaled-down version of IIS 5.1. You will likely have to go to the Control Panel >>

Add/Remove Programs, Add/Remove Windows Components to configure this service, as it's not installed by default.

All "scaled-down" versions of IIS / PWS have the following restrictions: 10 connection limit, you cannot use host

headers / multiple web sites, and you cannot change the port of HTTP services.
How do I manage a session across multiple windows?

10,933 requests - last updated Monday, November 12, 2001

You can't do this reliably. This is because Internet Explorer has a setting called "browse in new process" which, if

enabled, forces a new session with each new window. Use a key in the querystring, tied to a cookie or a form, if

session state across windows is a necessity. See the fake volleyball store for a working example.

Microsoft says there is no direct workaround for this, but goes into more detail about how it can happen randomly in

Q196383.

Also, starting with IE 5.01, this setting is no longer in the Advanced Options, but pre-determined -- depending on

the amount of RAM in the system. This is described in Q240928.

The short version: don't rely on maintaining sessions across multiple windows.
How can I give them a better 404 message?

9,765 requests - last updated Monday, April 15, 2002

That boring 404 error message leaves a lot to be desired. Wouldn't it be nice to be able to log those errors, find out

where the bad links are coming from, and most importantly, inform the user that the URL they entered is incorrect

(and, where applicable, suggest to them where they should have gone)?

Using a feature of IIS 4 and IIS 5.0, you can do all of these things and more. What you want to do is create a page

in your site called 404.asp. This is the page that will be presented instead of that stock grey page, so you'll likely

want to make this page look like the rest of your site. Here is an example.

One important thing to note is that this page should reference all images and links relative to your root folder, as the

404.asp file will actually execute in whichever folder it's called from. For example, if you have <img

src="image.gif"> and that image only exists in your root folder, then if someone calls /folder/no-exist.htm, the

image will show up as a broken link because it does not exist in /folder/ ... therefore you should always use <img

src="/image.gif"> or <img src="/images/image.gif">. Same goes for links, they should be relative to the root URL,

not to the folder that 404.asp resides in.

Once you have built a standard 404 page, you can make IIS intercept 404 errors and present this page instead by

following these steps:

1. Open Internet Services Manager

2. Expand the trees until you see Default Web Site

3. Right-click Default Web Site and choose Properties

4. Highlight the Custom Errors tab

5. Scroll down to 404 and click Edit Properties...

6. Choose URL from the Message Type listbox

7. In the URL> textbox, type the relative path of your 404 page

(Starting with the slash (/) that indicates your root)

You may want to add a bit of flexibility to this code; for example, you can display the page they were trying to reach

by accessing the querystring:


<%
b = Request.ServerVariables("QUERY_STRING")
msg = ""
if len(b) > 0 then
' Take out "404;" from querystring
msg = ", " & right(b,len(b)-4) & ","
end if
Response.Write "Sorry, the URL you were looking for" & msg & " is not available."
%>

Keep in mind that once you hit the 404 ASP page, you no longer have access to any anchor or querystring

information they originally asked for.

There are many other things you could do, such as send an automated e-mail or append a log file every time

someone hits a 404. After collecting data on non-existent pages that are queried often, you could anticipate them by

checking the value of b above and suggesting where they should go instead.

For a more in-depth article on building a 404-centric application, see the following:

http://msdn.microsoft.com/library/en-us/dnvb600/html/404track.asp

Note that .NET Server, at least in the few builds following Beta 3, does not support URL for custom 404 error pages.

Still waiting on word about why this was left out, whether it will return before IIS 6.0 is released, and if not, what it

will mean for users upgrading and expecting to continue using URLs for this purpose.
Why do I get an HTTP Header or Object Moved error when trying to redirect?

9,759 requests - last updated Wednesday, September 11, 2002

Header Error

Response object error 'ASP 0159: 80004005'


Header Error. The HTTP headers are already written to the client browser.
Any HTTP header modifications must be made before writing page content.

The short answer is to execute any response.redirect calls before any client-side code, including the opening <html>

tag. The long answer is to use response.buffer = true first, which can allow you to display content before

redirecting...

Object Moved

When using Response.redirect with certain browsers, you can get the Object Moved error message. One way to

prevent this from happening is using response.clear first (note that buffering must be enabled):

<%
Response.Expires = 0
Response.Buffer = true
' ...
Response.Clear
Response.Redirect "http://www.domain.com"
%>

This is an undocumented 'feature' of IIS 4.0 (but fully documented in IIS 5.0).

Thanks to Michel Thiffault for submitting this information.


How do I detect ENABLED cookies / javascript?

9,477 requests - last updated Tuesday, July 2, 2002

There are several components which will allow you to determine if cookies or javascript are SUPPORTED. You can

even do it yourself, with minimal knowledge of browser versions (and where in the dev timeline these features were

introduced). But knowing whether or not they're supported is far less than half the battle: the key is to detect

whether or not they're ENABLED.

There is a component that tells you whether these things are enabled (NOTE: it does NOT do this exclusively from

the SERVER), and many other important facets of a unique user's browser. It's called BrowserHawk:

http://cyscape.com/products/bhawk/bcadv.asp

You can do this yourself as well, but it's a much lengthier coding process.

Cookies:

Set a cookie, redirect, and try to read that cookie. If you can't, then cookies are disabled. Here is some

sample code:

cookietest.asp:

<%
response.cookies("enabled")="1"
response.redirect("cookietest2.asp")
%>

cookietest2.asp:
<%
if request.cookies("enabled")="1" then
response.write("cookies are enabled")
else
response.write("cookies are not enabled")
end if
%>

You can also try code like this, on any but the first page accessed:

<%
cook = Request.ServerVariables("HTTP_COOKIE")
if len(cook)<2 or cook="" then
response.write("Your cookies are disabled.")
else
response.write("Your cookies are enabled.")
end if
%>

JavaScript:

Populate default.asp with a hidden form (with POST method, action=default2.asp) and a link to default2.asp

in <noscript> tags. Use JavaScript to post to the next page, then try and read from the request.form

collection. If it's empty, they used the link (and Javascript is disabled). One caution: Netscape 2.0 will

execute <script> as well as <noscript>. Here is some sample code:

scripttest.asp:
<form method=post action=scripttest2.asp name='hiddenform'>
<input type=hidden name=enabled value="1">
</form>

<form method=post action=scripttest2.asp name='visibleform'>


<input type=hidden name=enabled value="0">
<input type=submit value=" Proceed >> ">
</form>

<script>
document.hiddenform.submit();
</script>

scripttest2.asp:

<%
if request.form("enabled")="" then
' user bypassed checker
response.redirect("scripttest.asp")
else
if request.form("enabled")="1" then
response.write("scripting is enabled")
else
response.write("scripting is not enabled")
end if
end if
%>

This solution is a little messy, as most users will see the first page for an instant. However if this information is

crucial to the rest of your application, it is a reasonable trade-off.


How do I run ASP on other web servers besides IIS?

9,250 requests - last updated Thursday, September 12, 2002

While ASP runs best, easiest and completely on IIS with NT Server / Windows 2000, there are some products

available which allow you to run ASP on other web servers (and even other platforms).

ChiliSoft (www.chilisoft.com) have several ports for other platorms, including O'Reilly's, Apache and Netscape on NT,

and a couple even on Unix.

Halcyon (www.instantasp.com) seem to have a pretty decent offering as well.

Apache::ASP is a port for Apache that allows you to run ASP (only PerlScript is supported).

Finally, NodeWorks has a module to run ASP on Apache.


Does order matter when using <%%>, <script runat=server>, and different languages?

8,834 requests - last updated Monday, September 4, 2000

YES.

Run these two scripts:

(a)

<%@language="vbscript"%>

<script language="VBscript" runat=server>


response.write("1<p>")
</script>

<script language="jscript" runat=server>


Response.Write("2<p>");
</script>

<%
Response.write("3<p>")
%>

<script language="jscript" runat=server>


Response.Write("4<p>");
</script>

<script language="VBscript" runat=server>


response.write("5<p>")
</script>

(b)
<%@language="JScript"%>

<script language="VBscript" runat=server>


response.write("1<p>")
</script>

<script language="jscript" runat=server>


Response.Write("2<p>");
</script>

<%
Response.Write("3<p>");
%>

<script language="jscript" runat=server>


Response.Write("4<p>");
</script>

<script language="VBscript" runat=server>


response.write("5<p>")
</script>

Pay particular attention to the order of the numbers that display on the screen, and compare it to the order of

execution in your scripts. In the best case, by mixing the two techniques you could have odd displays such as this.

In the worst case, you could have functions that are undefined, because you actually called them before you

initiated them.

My biggest recommendation: don't mix <%%> and <script runat=server>. Use one or the other.

Tom Stone adds that this is the order of execution:

1. scripts in non-default language

2. scripts using <% %>

3. scripts in default language


What's the deal with IIS 5.0 and ASP 3.0?

8,675 requests - last updated Wednesday, December 13, 2000

I see these questions all the time, here are the answers:

1. Can I run IIS 5.0 and/or ASP 3.0 on NT4?

A. IIS 5.0 is not available for Windows 95, Windows 98, Windows ME or Windows NT 4.0. If you need

any of the new functionality in ASP 3.0, then you also need to upgrade to Windows 2000. IIS 5.0

cannot be downloaded... it is on the media CD for your OS, and can be installed by going to

Add/Remove Programs, Add/Remove Windows Components.

2. Does Windows 2000 Professional ship with PWS or IIS?

A. PWS does not exist in the world of Windows 2000. Pro, Server and Advanced Server all ship with IIS

5.0. There are still connection limitations when you run IIS from Professional (as with PWS on

Workstation).

3. Where else can I learn about IIS 5 and ASP 3.0? (A.K.A. what's the difference between IIS 4 and

IIS 5?)

A. Here are a couple of links to get you started:

Internet Information Services Features

IIS 5.0 Technical Overview

Leveraging ASP in IIS 5.0

Q222487 INFO: What's New in ASP with IIS 5.0

Q250979 IIS 5.0 Release Notes


How do I make sure people go to page x before page y?

8,661 requests - last updated Sunday, July 9, 2000

There are three valid answers to this, depending on how you want to handle it.

(1) If you need to make sure that someone has been to a.asp before you will process anything on b.asp, you can set

a session variable in a.asp that flags a.asp as having been visited. Check for this session variable in b.asp; if it

exists/is true, do the processing.

(2) If you need to make sure that someone goes directly from a.asp to b.asp, check

Request.ServerVariables("HTTP_REFERER") in b.asp. If it's not a.asp, then they didn't arrive here through the proper

route.

(3) If you need to make sure that b.asp is only accessed once per session, you can set a session variable that says

("been_here_already")=true ... in that page, check to see if that variable exists... if it does, that means they've

already been here.


How do I get the login name / username from the person visiting my page?

8,144 requests - last updated Friday, July 26, 2002

If you have disabled Anonymous access, then you should be able to retrieve the value from:

<%
Response.Write Request.ServerVariables("LOGON_USER")
' or
Response.Write Request.ServerVariables("AUTH_USER")
%>

Note that your users must be using Internet Explorer to support NT Challenge/Response (IIS 4.0) or Windows

Authentication (IIS 5.0+).

If you need to support Netscape as well, then you can access these variables by enabling Basic Authentication as

well as Windows Authentication. Note that this method of authentication is slightly less secure, since the password is

sent in plain text (with Windows Authentication, IE encrypts the password as it is being sent across).

If you can't disable Anonymous access, then there is a possible alternative, provided you're not using DHCP. If your

users have static IP addresses, you could store their usernames in a table and do a lookup against their IP:

<%
Response.Write Request.ServerVariables("REMOTE_ADDR")
%>

If you can't enforce either of those things, then you may have to resort to forcing your users to log in (even only

once, then storing a cookie). I suppose this depends on balancing the importance of knowing who is on the site

versus every user having to log in.


Why do I get script errors on one machine but not another?

8,116 requests - last updated Thursday, December 12, 2002

This usually occurs with new methods or functions, such as FileSystemObject, try{} or With. The cause is that you

have a script library that is older than the documentation or sample code you are working from. Here are the errors

you might receive:

Microsoft VBScript compilation error '800a03ea'


Syntax error

or

Microsoft VBScript runtime error '800a0030'


Error in loading DLL

or

Microsoft VBScript compilation error '800401f9'


Error in the DLL

or

Microsoft VBScript runtime error '800a01b6'


Object doesn't support this property or method: '<method or property>'

or

Microsoft VBScript compilation error '800a0400'


Expected statement

Keep in mind that there is always the outside chance that you misspelled the property/method, or are using a

component where that property or method is in fact not supported.

If you are running Windows 2000 or better, then it is almost certain you are either misspelling the method/property,

or using a method/property that doesn't exist in the component you are accessing. An exception might be new

methods introduced in the 5.5 or 6.0 scripting engines, such as JScript's new push method for arrays.
For information on comparing versions between machines, see Article #2133.

For information on when features were introduced, see these charts for VBScript and JScript.

To upgrade the scripting engines on a machine, see http://msdn.microsoft.com/scripting/.

For information on keeping your server up to date, see Article #2151.


Where can I host ASP pages for free (or at least cheap)?

7,930 requests - last updated Monday, November 18, 2002

Well, they say there's no such thing as a free lunch. In most cases, I'd have to agree. Most web hosts out there

expect something in return for their 'free' web space, usually in the form of mandatory advertising (often including

those blasted popups).

Below is a list of ISPs offering free web space on NT servers, allowing you to use ASP. Most have limitations, be it

performance, or space, or database access, and of course almost always advertising. But filter through them, you

might find one you like. Let us know if you find a great deal.

www.webhostme.com

www.brinkster.com

Action Jackson has a list:

http://www.actionjackson.com/hosts/results.asp?cost=0

You can also search for good deals at www.webhostdir.com and www.hostindex.com ... they might not be free, but

many are under $10/month (and/or have a "one-time, lifetime" fee). For example:

www.atfreeweb.com

www.atrax.ws/hosting

www.awebhosting.net

www.gzinc.com

www.hostingplans.com

www.sharpwebservices.com

www.usware.net
www.webstrikesolutions.com

www.2globalmart.com

www.cheap-web-hosting.g2gm.net

asp4free.ravenna2000.it/ (Italian)

A word of warning though: don't sell this kind of deal to a client. For your own personal web space, they might be

okay (and even then, I would stick to those who have a refund policy)... Experience has proven to me, time and

time again, that you get what you pay for. Once in a while you'll find a gem, but when it's someone else's business

on the line, it just isn't worth the risk...


How do I get IntelliSense to see ASP 3.0 methods?

7,689 requests - last updated Wednesday, October 31, 2001

Methods like Server.Transfer and Server.Execute are not in Visual InterDev 6.0's IntelliSense (the little drop-down

that completes methods and properties for you). Many people have asked why this is, especially when InterDev is

installed on Windows 2000 with IIS 5.0 and ASP 3.0 installed.

I will try to explain what is going on, from my limited vantage point. IntelliSense itself gets its information from type

libraries; in this case, it is a file with a .tlb extension. Since InterDev 6.0 does not detect which operating system /

web server version it is being installed alongside, it ships with ASP 2.0's type library, not ASP 3.0's -- hence the

currently missing method names.

So how do you get the new type library to be recognized by Visual InterDev? Is it even possible?

Yes, it is possible; observe the screen shot at left. It includes server.transfer and

server.execute.

How did I do it? There are a few hoops to go through...

1. Ensure that all Visual Studio components are closed.

2. Go to <path>\Microsoft Visual Studio\Common\IDE\IDE98\.

3. Rename asp.tlb to asp.tlbak (just in case).

4. Place this TLB file in that folder.

(Note also that Server.URLPathEncode, an undocumented method, has been removed from the new type library.)

The instructions for creating this TLB file are documented in Q261101 -- but the methodology behind it requires

OLEView and midl.exe to be installed and enabled on your machine. Also, if your machine isn't running ASP 3.0 (e.g.

you develop on NT 4.0 and deploy to Windows 2000), you have to copy the new asp.dll to your local machine... I

figured it would be easier to just give you the file. :-)

It seems that with the Visual Studio.NET Betas and Windows XP Professional (RTM Build 2600), you don't need to
add this type library at all.

Of course, if you are afraid of messing up your system, do not alter your environment. This operation is not officially

supported by Microsoft.
What is wrong with Request.ServerVariables("HTTP_REFERER")?

7,591 requests - last updated Friday, November 8, 2002

The situations where this servervariable works include the following methods of a browser loading a URL:

■ A straight HTML <a href>

■ A form submit (POST or GET) using a submit button or <input type=image>

■ A form submit (POST or GET) using JavaScript

The situations where it doesn't work:

■ A link from Favorites

■ A click on 'Home' or a Link with a defined URL

■ A JavaScript location.href or location.replace()

■ A page linked from HierMenus (details)

■ Typing the URL directly in the browser

■ Launching a clickable URL from an e-mail or Word document

■ Using Response.Redirect / Server.Transfer

■ Using Response.AddHeader or <meta http-equiv=refresh> to redirect

■ Loading the URL with XML (see Article #2173)

Obviously, this variable cannot be relied upon for many situations.

Here is the code I used to test with. Ref.asp contains the following code:

<%
response.write "Referer: " & request.servervariables("http_referer")
%>

And ref.htm contains the following:


<%
Response.AddHeader "Refresh", "10;URL=ref.asp"
' interchanged with the <meta> version:
%>

<meta
http-equiv='refresh'
content='10;URL=ref.asp'>

<form method=GET action=ref.asp name=getform>


<input type=submit value=' Go there (GET) >> '>
<input type=image style='cursor:hand'>
</form><p>

<form method=POST action=ref.asp name=postform>


<input type=submit value=' Go there (POST) >> '>
<input type=image style='cursor:hand'>
</form><p>

<a
href='ref.asp'>Go
there - straight link</a><p>

<a
href='#'
onclick='window.location.href="ref.asp";return false;'>Go
there - javascript href</a><p>

<a
href='#'
onclick='window.location.replace("ref.asp");return false;'>Go
there - javascript replace</a><p>

<a
href='#'
onclick='document.getform.submit();return false;'>Go
there - javascript GET</a><p>

<a
href='#'
onclick='document.postform.submit();return false;'>Go
there - javascript POST</a>

You'll notice that in Netscape 4, the referer is translated properly in all cases. With this exception, I think we're all

glad, overall, that the number of users with Netscape 4 is quickly diminishing! :-)
When I run a page in my browser, why does the ASP code not execute?

7,358 requests - last updated Thursday, September 5, 2002

There is usually a limited number of reasons why this would happen. They are as follows:

■ you do not have IIS or PWS installed

■ you did not save your page with an .asp extension (or a file type associated with asp.dll)

■ you did not place your ASP page in your web root / structure, or you put it in a place that does not have

"execute script" permissions

■ you accessed the file by typing c:\<path>\<file>.asp or by double-clicking it within Windows Explorer (ASP

pages need to be interpreted by a web server, not a file system)

■ you expected to response.write code using <% varname %> syntax when you should use <%= varname

%> syntax (that equality sign is important)

■ you tried to preview your ASP page in a WYSIWYG editor (like InterDev or FrontPage)

If none of the above applies to you, then it is likely that you have some kind of misconfiguration (or else you are

getting an error message). Post your configuration, code and any errors you get to

microsoft.public.scripting.asp.general and someone will help you solve your problem.


How can I track when my site is added to a user's favorites?

7,353 requests - last updated Tuesday, April 16, 2002

I have been using custom 404 error pages for a long time -- I wrote an article on the technique for MSDN in October

of 1999, and have a straightforward example in Article #2162. I have tried several experiments using this feature,

and have learned some interesting things from my research.

One of these is that when you add a favorite in Internet Explorer, the browser searches for the web site's "favorites

icon" - or favicon.ico. (For more info, see www.favicon.com.) This is how your favorites end up with branded icons

for some of the web sites you go to, and how http://www.msn.com/ and other web sites have their icon in IE's

address bar.

Note that you can use a different ICO file if you prefer... favicon.ico is only the default. You can override it

with the following tag:

<LINK REL="SHORTCUT ICON" HREF="http://www.wherever.com/iconName.ico">

Well, if the browser searches for this file and can't find it, you can capture the event with a custom 404 page. I

simply created a table to increment a counter:

CREATE TABLE FavIcon


(
cnt INT
)

Then a stored procedure to handle the updates:

CREATE PROCEDURE FAQ_bumpFavIcon


AS
BEGIN
UPDATE FavIcon SET cnt = cnt + 1
END
And In my custom error page for 404 errors, I have the following code:

<%
' ... valid connection "conn" established

qs = lcase(Request.ServerVariables("QUERY_STRING"))
if right(qs,12)="/favicon.ico" then
conn.execute("EXEC FAQ_bumpFavIcon")
end if
' ...
%>

I'll leave it as an exercise to the reader how I display it on the home page. For most people, this should be pretty

self-explanatory. :-)
How can I stop Photoshop from opening ASP files?

6,955 requests - last updated Sunday, July 9, 2000

This is a known issue, but only occurs if you install Photoshop AFTER applications such as Visual InterDev (or,

shudder, FrontPage). When setting up a new system, do yourself a favor, and be sure to install Photoshop FIRST.

Here are the steps to fix your problem the right way:

(1) start, run, regedit

(2) Expand HKEY_CLASSES_ROOT

(3) Find a folder named ".asp"

(4) Right-click the folder and choose "delete"

(back up your registry if you're not sure)

(5) Close the registry editor

(6) Next time an asp page is double-clicked, you will get the "Choose a program to run this file" window. Choose the

application that you would like to use to edit the code (e.g. Notepad or InterDev) and make sure that the check box

on the bottom ("Always use this file...") is checked.


Why does my page render (properly) in IE and not in Netscape?

6,532 requests - last updated Sunday, July 9, 2000

Netscape is much more stringent about properly formatted HTML documents. If, for example, you use a <table> and

there is no closing </table>, the table will not be displayed. If Netscape is in your target audience, make sure that

all HTML tags (at least those that require it) have closing tags. Mind you, it's not a bad habit to follow even when

Netscape ISN'T in your target audience.


How do I increase timeout values?

6,241 requests - last updated Sunday, September 15, 2002

For most timeout situations, my first suggestion would be to improve the code's efficiency so that it does't time out

at all. I realize, however, that some timeout issues are beyond the developer's control (e.g. waiting for a response

from a remote server). Also, there are many scenarios where you would want to increase the session timeout so

that users can "stay" longer.

Server.ScriptTimeout

This value indicates how long, in seconds, the server will let an ASP script run until it is stopped by the server. The

default value is 90 seconds. An error occurs if a script requires more time than this value allows (see Article #2366),

but you can override the default. To change this value in an individual ASP page, you can add this code:

<%
Server.ScriptTimeout = 180
%>

To change this value for an entire application, open Internet Services Manager, go to the Home Directory tab of the

application, click on configuration, and alter the ASP Script timeout field on the "App Options" tab. If you do not

have access to the web server directly, you can also use ADSI to change this value on a per-application basis. Keep

in mind that with this method you can only override the default with a value GREATER than that stored in the

metabase - if you try to lower the current value, the change will not take effect.

The minimum value for ScriptTimeout is 0. If you try to set it to a negative number (e.g. -1 is often associated with

"infinite"), you will get a 0x80004005 error.

The maximum value for ScriptTimeout is 2^32-1, or 2147483647. If you try to set it to 2147483648 or higher, you

will get the following error:

Microsoft VBScript runtime (0x800A0006)


Overflow: 'server.scripttimeout'
If you need this much time, you need to investigate your script, because it must be horribly inefficient. And no user

(or even browser) will ever wait that long for a page to render.

Session.Timeout

This setting controls how long, in minutes, a user's session will last. While it is wise to keep this value short for

efficiency's sake, there are cases where that's just not enough time for users to get things done in your application

(for example, if you have a client-side tool where the user is changing properties but not making requests to the

server until they are done). The default is 20 minutes, and once 20 minutes of inactivity has occured, the session

expires and all session variables are lost. You can increase the session timeout in an ASP page or in global.asa's

Session_onStart() method with the following code:

<%
Session.Timeout = 45
%>

To change this value for an entire application, open Internet Services Manager, go to the Home Directory tab of the

application, click on configuration, and alter the Session timeout field on the "App Options" tab. This metabase

setting is also exposed to ADSI.

conn.connectionTimeout

Where conn is an ADODB.Connection object that is not yet open, the connectionTimeout property will indicate the

amount of time, in seconds, to wait for an ASP application to initially connect to the data source. The default is 15

seconds; to override the default, use syntax as follows:

<%
Set conn = Server.CreateObject("ADODB.Connection")
conn.ConnectionTimeout = 120
conn.Open <connectionString>
%>
commandTimeout

CommandTimeout tells the server how long to wait, in seconds, for completion of any command sent to the data

source. This value is editable before and after the connection has been opened. The default is 30 seconds, but you

can override it like this:

<%
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open <connectionString>
conn.CommandTimeout = 120
%>

Note that you can also apply a commandTimeout value to a command object, and it will behave independent of the

commandTimeout value associated with the connection object. So for specific stored procedures or other commands

being executed explicitly through the ADODB.Command object, you could have a longer timeout than that of the

connection object. The syntax for the command object is almost identical:

<%
Set cmd = Server.CreateObject("ADODB.Command")
cmd.CommandTimeout = 120
%>

Note that Oracle drivers do not support the commandTimeout property, up to and including MDAC 2.7. See Q251248

for more information.

XMLHTTP timeouts

For information on increasing or decreasing the time allowed for the XMLHTTP objects to retrieve a response from a

remote server, see Article #2407.


Why does REMOTE_HOST return an IP address instead of a name address?

6,241 requests - last updated Monday, July 29, 2002

IIS does not automatically do reverse DNS lookups - by default, Request.ServerVariables("REMOTE_HOST") is

populated with the same value as Request.ServerVariables("REMOTE_ADDR") - which is simply an IP address.

According to Q245574 and Q297795, you can configure IIS to handle DNS lookups. Barring that ability, here are

some components that will facilitate it without altering IIS:

AspDNS (ServerObjects)

ASPDNS

BrowserHawk

DNS LookUp

DSDNS

DynuDNS

HexDNS

LyfDNS

Simple DNS+Traceroute

Remember that reverse DNS lookups on every request can be a significant burden on your server... so only

implement one of these solutions if you really need it! Also remember that performing a reverse DNS lookup doesn't

necessarily give you accurate results. Statistics Server, for example, returns the state that AT & T's regional office is

in, not the state where they provide our bandwidth.


How do I prompt a "Save As" dialog for an accepted mime type?

6,207 requests - last updated Monday, July 8, 2002

Often you will want a user to download / save a TXT, XLS, GIF, HTML or other file type that the browser would

normally simply open and display within the browser (either from an HREF, a response.redirect, or a save target as).

Here is one method I've used to force a prompt, in a file called ml1.asp:

<%
fn = "ml1.jpg"
FPath = "c:\" & fn
Response.ContentType = "application/asp-unknown" ' arbitrary
Response.AddHeader "content-disposition","attachment; filename=" & fn
Set adoStream = Server.CreateObject("ADODB.Stream")
adoStream.Open()
adoStream.Type = 1
adoStream.LoadFromFile(FPath)
Response.BinaryWrite adoStream.Read()
adoStream.Close: Set adoStream = Nothing
Response.End
%>

And from the page where you want to launch this Save As dialog, you can do any method of redirection. Here are a

few examples:

<%
response.redirect("ml1.asp")
%>

<meta http-equiv='refresh' content='0;url=ml1.asp'>


<a href='ml1.asp'>Download ml1.jpg</a>

<script>
window.location.replace('ml1.asp');
</script>

Now keep in mind that the user can just decide to open it if they choose.
Why am I having problems with Server.Execute and/or Server.Transfer?

6,153 requests - last updated Friday, September 6, 2002

If you are running IIS 4.0 / PWS 4.0 (or earlier), you will get the following error when trying to use server.transfer:

Microsoft VBScript runtime error '800a01b6'


Object doesn't support this property or method: 'Server.Transfer'
/<file>.asp, line <line>

In IIS 4.0, use Response.Redirect, since Server.Transfer and Server.Execute were introduced in IIS 5.0.

If you are trying to pass QueryString values to Server.Transfer or Server.Execute, you will receive the following

error:

Server object error 'ASP 0173 : 80004005'


Invalid Path Character
/<file>.asp, line <line>
An invalid character was specified in the Path parameter for the MapPath method.

Workarounds include using session variables to keep passing parameters between pages, using Response.Redirect

instead, or making sure that the *source* page is called with QueryString values. For example:

Source.asp

<%
server.transfer "target.asp"
%>

Target.asp
<%
Response.Write(Request.QueryString("foo"))
%>

Now load http://localhost/source.asp in your browser, and then add the QueryString parameter ?foo=1. You will

notice that pages executed by the transfer and execute methods still have access to the QueryString parameters of

the initial page.

If you try to use an absolute URL, in IIS 5.0 you will get the following error:

Server object error 'ASP 0235 : 80004005'


Server.Transfer Error
/<file>.asp, line <line>
Invalid URL form or fully-qualified absolute URL was used. Use relative URLs.

or

Server object error 'ASP 0231 : 80004005'


Server.Execute Error
/<file>.asp, line <line>
Invalid URL form or fully-qualified absolute URL was used. Use relative URLs.

In IIS 6.0 you get the MapPath problem mentioned above; assumedly, they changed the order of

parsing/interpretation so that the : character actually prevents the call from being made at all.

Since you can only transfer to, or execute, ASP pages on the same application, there is no reason to use a fully

qualified URL. If you need to go to a server (even if it's the same machine) by a different domain name, use

Response.Redirect.

One other difference between Response.Redirect and Server.Transfer / Server.Execute is that if the target page uses

variables from the Request.ServerVariables collection (e.g. PATH_INFO, SCRIPT_NAME and URL), they will reflect

the URL of the *source* page using the newer methods, while Response.Redirect with reflect the URL of the

*target* page.
Can I run IIS on Windows Millennium or Windows XP Home?

6,118 requests - last updated Friday, December 6, 2002

Many people switching from Windows 98 to Windows ME or XP Home Edition have been disappointed to find that

PWS is not included. Microsoft also does not support the installation of any web server on these products. Many

people have witty answers like "install Apache" or "switch to Linux." These might be realistic workarounds in their

eyes, but are clearly not an option for most people who would be asking the question in the first place.

Here are explanations from Microsoft's Knowledge Base:

Q266456 Personal Web Server Is Not Included with Windows Millennium Edition

Q304197 Personal Web Server Is Not Included with Windows XP Home Edition

The latter KB article, when it was numbered Q310090, actually described how to work around the problem in

Windows XP. They have since removed that article and renumbered it, and the advice they gave has vanished

without a trace (that'll teach me to not copy it locally!). Their current statement is that the only way to get web

server support within XP Home is to upgrade from a previous 9x-based OS with a web server installed.

If you're going to go that route (or if you have yet to upgrade to XP Home), here is a helpful link for getting the

Option Pack to install in Windows ME:

http://billsway.com/notes_public/PWS_WinMe.txt

(If you have Windows 95 or Windows 98, and don't have a web server installed, see Article #2075 for info on

configuring the Option Pack.)

On the other hand, if you want to experiment some, you can see this advice from Richard Sandoz, who explains how

to get IIS working on XP Home:

http://tinyurl.com/3ahy

Finally, just for giggles, here is a tutorial for setting up an Apache web server on XP Home:

http://rain.prohosting.com/~starman2/apache.shtml
If you need ASP support, I recommend developing on Windows 2000 Pro or Windows XP Pro. If your machine can

support it, I would use Windows 2000 Server or Advanced Server. You should use whatever environment would

most accurately reflect your true production setting. I don't know too many commercial web sites that are running

on Windows 9x, but I do know that there are several problems reported daily by people developing in one

architecture and deploying on another... these are mostly due to security / permissions issues on the deployment

machine(s) that didn't exist in the development environment.


Why is Netscape slow in IIS 5.0?

6,104 requests - last updated Friday, March 16, 2001

A minor change introduced with IIS 5.0 may, in obscure situations, cause Netscape to render a page a bit slowly

(though never by a minute, which I believe was an exaggeration by one individual). However, Microsoft did not to do

this to thwart Netscape. All they did was change the response.buffer default property to "TRUE" (it was "FALSE" in

IIS 4.0).

When response.buffer is set to TRUE, the entire page is processed and stored in a "buffer" -- not being sent to the

client until the entire page is processed. When response.buffer is set to FALSE, the page is "streamed" to the client

as it is processed - so you will some lines of text, then the server will churn out some more lines and they will

appear, and so on until the page is finished.

So, if you are having this problem, try setting the following at the top of the page (you can also uncheck "Enable

buffering" in the configuration/app options tab of any application, or the default web site, in Internet Services

Manager):

<%
Response.Buffer = False
%>

In addition to the above solution, Peter Krantz tells us the following:

If you are running Windows 2000 you can easily fix this by bringing up the task manager, selecting the

Process tab, right-clicking the netscape.exe image and setting the priority to Low which means that the IIS

will be prioritized over netscape to process the form.

Remember that, in either case, Netscape is always going to have problems with HTML rendering, particularly with

complex tables and CSS. This is just a fact of the Internet: Netscape has a terrible rendering engine (which makes it

a GREAT test browser). I have found that, the more complex the page, the longer Netscape will take to load it... and

this is independent of web server settings, web server software, and even platform.
What do I need to know about Response.Redirect?

6,091 requests - last updated Tuesday, November 6, 2001

■ Basics of Response.Redirect

■ Why can't I...

■ Is there an alternative?

Basics of Response.Redirect

When you request a page from a web server, the response you get has some headers at the top, followed by

the body of the page. When viewed in your browser the headers are never seen, but are used by the

browser application. I have the following page called test.asp;

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>

</BODY>
</HTML>

When I request that from the web server this is the reply I get;
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:07:44 GMT
Connection: close
Content-Length: 134
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=OMCJFABDNCDLLBKAPNHJBKHD; path=/
Cache-control: private

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>

</BODY>
</HTML>

The first line returns the status of the response, in this case "200 OK" which means everything is fine. The

following lines are headers, these are in the format of

Name: Content

So in our example the web server is identifying itself as Microsoft-IIS/5.0, it also sends its date and time,

the content type and it also instructs the browser to store a cookie. This cookie contains your session ID and

is used by IIS to remember who you are. After the headers there is a blank line then the actual HTML to be

shown in the browser.

If I request a page that does not exist then we get this back;
HTTP/1.1 404 Object Not Found
Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:11:54 GMT
Connection: close
Content-Type: text/html
Content-Length: 3243

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">


<html dir=ltr>

<head>
<style>
a:link {font:8pt/11pt verdana; color:FF0000}
a:visited {font:8pt/11pt verdana; color:#4e4e4e}
</style>

<META NAME="ROBOTS" CONTENT="NOINDEX">

<title>The page cannot be found</title>


...

As you can see, the status is now "404 Object Not Found" and the HTML is generated for us by the web

server. So the web server uses different response status types to inform the browser the nature of the

response itself. Let's modify our test.asp code to read this;

<%
response.redirect "test2.asp"
%>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>
</BODY>
</HTML>

Note the code at the top of the page that does a "response.redirect". If we request this page we get the

following;

HTTP/1.1 302 Object moved


Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:16:35 GMT
Connection: close
Location: test2.asp
Content-Length: 130
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=ANCJFABDFLBLHMKIJOIOKJDM; path=/
Cache-control: private

<head><title>Object moved</title></head>
<body><h1>Object Moved</h1>This object may be found <a HREF="test2.asp">here</a>.</body>

The status is "302 Object moved" and note that the actual HTML following the Response.Redirect is not sent

to the client. After all, if the page is going to be redirected why send any content? When Internet Explorer

gets this type of response it gets the file to be directed to via the Location header

Location: test2.asp

And issues another request to get test2.asp. IIS does something clever for us here as well; just in case your

browser does not understand 302 headers it generates HTML giving the user a link that they can manually

click on. If you are going through a firewall you may have actually seen this code sometimes in your

browser. However Internet Explorer doesunderstand 302 headers so shows no page, but simply requests

the new page instead.

That is the simple mechanism where by the web server responds to your requests for pages, however IIS

gives us two delivery mechanisms; buffered or non-buffered. When buffering is off IIS sends HTML to the

client as it is generated. With buffering on, IIS holds all HTML in a buffer until the page has finished

processing, it then hands the HTML to the client in one big batch.
Try this code;

<% Response.Buffer = true %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
for i = 1 to 10000
Response.Write i & "<br>" & vbCRLF
next
%>
</BODY>
</HTML>

Notice that we are programmatically setting the buffering mode to True, i.e. we want the page buffered.

When you navigate to this page the browser will sit and wait, and all of a sudden you'll see 10000 lines in

your browser. If you update the code to turn buffering off;

<% Response.Buffer = false %>

Then you will see the page gradually grow in size until all 10000 lines have been written. Note that IIS4 and

IIS5 have different default options for buffering. In IIS4 it is off by default, and in IIS5 it is on by default.

This means that if you omit the "Response.Buffer =" code then IIS4 will default to false, and IIS5 to true.

Let's look at how buffering and redirection can come into conflict. In the following sections I'll explicitly turn

buffering on or off so that the code works the same under IIS4 and IIS5.

Update test.asp to read;


<% Response.Buffer = false %>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>

<%
Response.Redirect "test2.asp"
%>

</BODY>
</HTML>

The response.redirect is now mid-way through the page. If we view this page we see the following in the

browser;

Hello

Response object error 'ASP 0156 : 80004005'

Header Error

/Examples/test.asp, line 12

The HTTP headers are already written to the client browser. Any HTTP header
modifications must be made before writing page content.

Why do we get this error? Remember that a normal page is sent with "200 OK" in its response headers, and

a redirect is "302 Object moved". When IIS hits the first piece of HTML output;

<HTML>
it deduces that this is a standard "200 OK" page and that the browser should be sent output as it is

generated. So it sends this;

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:07:44 GMT
Connection: close
Content-Length: 134
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=OMCJFABDNCDLLBKAPNHJBKHD; path=/
Cache-control: private

<HTML>

As more HTML is generated it is also sent to the browser to be displayed. When it hits the response.redirect

command it has a problem. In order to re-direct it needs to send a "302 Object moved" header but it can't

as it has already sent a "200 OK" header, thus the error about not being able to modify the headers.

Now change the code so that buffering is on;

<% Response.Buffer = true %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>

<% response.redirect "test2.asp" %>

</BODY>
</HTML>

And try again, this time it works. With buffering on, IIS stores all output in a buffer until the page has

completed. So by the time it gets to the redirect it will have this in its buffer;
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:07:44 GMT
Connection: close
Content-Length: 134
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=OMCJFABDNCDLLBKAPNHJBKHD; path=/
Cache-control: private

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<p>Hello</p>

However it has not sent anything to the client yet. So when it hits your redirect it simply throws away

anything in it's buffer and replaces it with this instead;

HTTP/1.1 302 Object moved


Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:16:35 GMT
Connection: close
Location: test2.asp
Content-Length: 130
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=ANCJFABDFLBLHMKIJOIOKJDM; path=/
Cache-control: private

<head><title>Object moved</title></head>
<body><h1>Object Moved</h1>This object may be found <a HREF="test2.asp">here</a>.</body>

That is sent to the client and the browser can be redirected.


So to avoid the error when doing a redirect, either put the redirect before any output, or turn on buffering.

Remember that if you are using IIS4 you have to turn on buffering programmatically, if you use IIS5 it is

done by default. However if you rely on buffering being turned on you should always explicitly add it to your

code. That way you won't get problems when you move to a different web server or when Microsoft decide

to change the default again. You can also configure buffering to be on or off by default via the IIS MMC.

Why can't I...

...redirect to a frame?

HTTP is a request/response technology. The browser requests a page from the server, and the browser

displays the response. For a page to appear in a frame, it is that frame that must make the request. You

can't make a request in one frame and have the response piped to another. Only the requester can get the

response.

To work around this you should create HTML that does the frame manipulation on the client;

<html>
<body>

<p>My page is here</p>

<script>
parent.targetframename.location.replace('page2.asp');
</script>

</body>
</html>

This will cause "targetframename" to request page2.asp, thus showing the response in that frame.

...POST variables when doing a redirect?

When you submit a FORM with the GET method, your browser does not do anything clever, it simply
appends the FORM elements to the end of the page in the ACTION parameter. When you submit a FORM

using the POST method an entirely different mechanism is used. When your browser gets the 302 response,

it reads the new location from the Location header and simply requests that page. You can simulate a GET

submission by simply tagging on the parameters yourself, thereby emulating what the browser does. So if

you do;

<% Response.Redirect "test2.asp?myparam=123" %>

the resulting header looks like this;

HTTP/1.1 302 Object moved


Server: Microsoft-IIS/5.0
Date: Mon, 19 Mar 2001 15:16:35 GMT
Connection: close
Location: test2.asp?myparam=123
Content-Length: 130
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGQQJWO=ANCJFABDFLBLHMKIJOIOKJDM; path=/
Cache-control: private

<head><title>Object moved</title></head>
<body><h1>Object Moved</h1>This object may be found <a
HREF="test2.asp?myparam=123">here</a>.</body>

So the browser requests "test2.asp?myparam=123", thus passing on your parameters. Emulating a POST,

however, is not that simple and the Redirect process simply does not support it.

The workaround is similar though, you should generate the appropriate HTML to do the job yourself;
<html>
<body>

<form name=frmTest action=page2.asp method=POST>


<input type=hidden name=txtMyVar value="Hello">
</form>

<script>
document.frmTest.submit();
</script>

</body>
</html>

The only problem with this is that you are relying on scripting support from the client.

Is there an alternative?

Yes, IIS5 gives us an alternative to doing a response.redirect. You can now use the Transfer or Execute

method of the Server object. So instead of doing response.redirect "page2.asp" you can simply

server.transfer "page2.asp". It is all done at the server so will work even if you have already sent some

output to the client.

Another option is to emulate a redirect using a COM object that lets us request another ASP page within our

script code. For more details on this see Article #2173.

This article submitted by Adrian Forbes - April 18, 2001


Why does IIS 5.0 stop serving ASP pages?

6,035 requests - last updated Sunday, October 28, 2001

You might find that your machine becomes corrupt occasionally. IIS is still running, most functions work (HTML

pages and images etc. still serve correctly), but ASP pages no longer run properly. These scripts seem to timeout

endlessly, never returning an error message or timeout warning (unless generated by the browser).

What may have happened is that your Windows Access Method (IWAM_<machine>) account has gone out of sync,

or your application has changed such that IWAM can no longer function correctly. The easiest solution, according to

Stefan B. Schachner, is to re-sync the IWAM account. You can do this in a couple of quick steps at a command

prompt:

■ CD to the AdminScripts folder (under Inetpub)

■ Type cscript synciwam.vbs

For more information on the IWAM account, and more verbose steps at producing the same results as above, see

Q195956 and Q236855...

After the above failed, one thing that has worked for some people is to change the Application Protection level of the

Default Web Site to Low. This is found by right-clicking the application, choosing Properties, and moving to the

bottom of the Home Directory tab.

You may also want to check out Article #2180 if your pages use FileSystemObject and you have Norton Anti-Virus

installed.

Finally, if you only have certain ASP pages that are hanging the system, you'll want to check them for infinite loops.

The most common is:

<%
do while not rs.eof
' stuff
loop
%>
Since there is no rs.movenext right after the 'stuff line, the ASP code will just keep processing the same record over

and over again, until you flip the kill switch.


Can I detect plug-ins/ActiveX controls from ASP?

5,870 requests - last updated Monday, July 10, 2000

Not with ASP alone. You can use client-side code, to some extent. Here is an example for Nav3+ and IE 3.02+ that

detects Flash 2.0:

<%
' This ASP section pre-determines which
' script to send ... VBScript or JavaScript

a = lcase(request.servervariables("http_user_agent"))
if instr(a,"msie")>0 then
if instr(a,"98")>0 or instr(a,"95")>0 or instr(a,"nt")>0 then
ie32="true"
' IE 3 or greater on 32-bit
end if
elseif instr(a,"mozilla/3")>0 or instr(a,"mozilla/4")>0 then
if instr(a,"opera")<=0 then
nn="true"
' NN3 or greater
end if
end if

if ie32 then
%>

<script language="vbscript">
if scriptEngineMajorVersion > 1 then
on error resume next
FIn=(IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash")))
if FIn then
msgbox "Flash Installed!"
else
msgbox "Flash not installed."
end if
end if
</script>
<%
elseif nn then
%>

<script language="JavaScript">
FIn = navigator.plugins["Shockwave Flash 2.0"];
if (FIn)
{
alert("Flash Installed!");
}
else
{
alert("Flash not installed");
}
</script>

<%
end if
%>

Of course instead of alert and msgbox statements, you would document.write <object> and <embed> tags or

alternate content, depending on the outcome of your test.

If this seems like too much work, you may consider looking at BrowserHawk which allows you to detect all kinds of

client-side plug-ins and controls from ASP.


How do I make my ASP page pause or 'sleep'?

5,841 requests - last updated Friday, June 21, 2002

There is a free component that will help you do this; it is called WaitFor 1.0 and is available at ServerObjects Inc.

If you are using SQL Server (or have access to one), you can try out the followign script (adapted from a post by

Pierre W):

<%
Set conn = Server.CreateObject("ADODB.Connection")
' note that you don't need to indicate a database:
conn.Open "<connection string>"

' indicate a number of seconds, up to 59


sleep = 10

' make sure timeout doesn't expire!


conn.commandTimeout = sleep + 5

' if you neede more than 59 seconds, you will need to adjust the SQL:
conn.Execute "WAITFOR DELAY '00:00:" & sleep & "'"

conn.close: Set conn = Nothing


%>
How do I make Visual InterDev's debugging features work?

5,834 requests - last updated Wednesday, January 30, 2002

Visual InterDev's debugging features are useful for some people (I am not one of those people). But many people

have problems getting them working, and keeping them working. This can be due to misconfigured systems from the

get-go, or from installing software afterward that causes conflicts or errors that may not be visible.

One common misconception is that if you can install VI, you can run the *server* debugging tools. AFAIK, VI's

debugging tools only run on Windows NT and Windows 2000... they will not run on Windows 9x clients (though client-

only debugging can function). Also, they work best if they are running ON the server that is hosting the ASP files.

Remote debugging works -- allegedly -- but reports I've heard grade it mediocre at best.

If you've already installed InterDev's server extensions, search for a file called VIDDBG.exe. From a command

prompt, issue the following code:

<drive>:\<path>\VIDDBG.EXE /server > videbug.txt

This will run diagnostics on the debugging tools, and may help you determine why they're not working. In any case, if

you're going to be fixing them, you'll likely need to (re-)install the server extensions.

If you're running NT 4.0, you'll want to reinstall SP4 or greater before starting. Next, find the file SETUP.EXE in the

\VID_SS\ folder on the CD-Rom (this will be on Disc 1 of Visual InterDev, or Disc 2 of Visual Studio Pro or

Enterprise). Double-click this file to launch the installation. Finally, reinstall the latest service pack for Visual Studio

(at the time of writing, that is SP4).

For more information on Visual InterDev debugging, see the following articles:

Q244272 INFO: Visual InterDev 6.0 Debugging Resources

Microsoft Visual InterDev 6.0 Debugging

If you are getting errors (e.g. 'Unable to contact web server') or are having trouble making debugging work, see the

following KB articles:
Q220166 PRB: Troubleshooting "Unable to Contact Web Server" in Visual InterDev

Q195954 PRB: "Unable to Contact Web Server" Error Creating New Project

Q194013 PRB: No Warning From VID Debugger If Connection to Server Fails

Q167661 PRB: Visual InterDev Cannot Connect to PWS or Share

Q191560 FIX: Contacting Web Server to Open Web Project Never Times Out
Why does global.asa not fire?

5,736 requests - last updated Tuesday, January 9, 2001

Well, that depends on which part of global.asa is failing. If it is only session_onEnd(), then see Article #2078 for an

explanation of the problem.

If none of the methods in global.asa are firing, then there are two common scenarios:

(a) there is a syntax or other error in the global.asa file, or

(b) the global.asa file resides in a folder that is NOT marked as an application.

If (b) is not the problem, I suggest extracting the subs by themselves and running them in an independent ASP

page... this will tend to weed out errors.


How do I fix the ::$DATA bug?

5,714 requests - last updated Monday, September 18, 2000

The only way this could be a concern for you is if you're still running NT 4.0 with Service Pack 3. <shudder>

Eliminate the problem by installing Service Pack 6a. More info here:

Q197464 How to Detect the ::$DATA Attack in IIS Log Files

Q232449 Sample ASP Code May be Used to View Unsecured Server Files
How do I access my server's registry from an ASP page?

5,651 requests - last updated Sunday, October 28, 2001

Here are some components available to do read from and/or write to the registry:

ADEX Registry

ASP MagicBundle

DES RegEdit

MS Registry Access 1.0

RegEdit Library

Stonebroom.RegEx
Should I use VBScript or JScript for ASP?

5,589 requests - last updated Monday, February 4, 2002

This question is asked fairly regularly. Really, it comes down to preference. If you come from a VB or VBA

background, VBScript would probably be the logical choice. If you're already familiar with JScript on the client side,

or have a strong background in C, C++ or Java, then it's the language you should use. However, if you anticipate

asking or looking for help with code issues, that might tip the scales back to VBScript. This is because most samples

you'll find online, and most people hanging around the newsgroups to help you, deal with VBScript. In any case,

here is my short list of pros for each language:

VBScript

■ functionality previously missing (eval, execute) now in v5

■ more intuitive object creation / destruction

■ outside of MSDN, most samples and help are geared to VBScript

JScript

■ code reuse (since most of you will be writing client-side JScript)

■ array has built-in sort method, which has proven useful to me at least

For those planning large, high-traffic sites, you might want to take into account that each language is proficient at

certain things. So, if performance is an issue, you should consider that:

■ Peter Hanus discovered that VBScript is faster in almost all operations except pattern-matching and bit-

shifting (these results are published in an article on ASPToday.com, which charges for access).

■ In another article, greyMagic software goes to great lengths to show that, in their experiments, JScript is

faster.

Personally, I prefer VBScript. I like its more English-like syntax, its error handling, and collection enumeration. I

don't recall ever using both languages in the same page, though I do have a few pages in a recent application that

are written completely in JScript (one such page puts a list of files from a folder into an array, sorts them into

alphabetical order, and returns them to an application). To be completely sold on VBScript, you may just have to

buy a ridiculously overpriced subscription to ASPToday.com.


Manohar Kamath has written a thorough article on this topic, outlining the pros and cons of each language:

http://www.kamath.com/columns/my3cents/mtc002_scripting.asp
Why won't my session variables stick?

5,394 requests - last updated Saturday, September 7, 2002

Session variables can stop working (or never start), or your sessionID can change from page to page, for a variety

of reasons. Here is a short collection of items you can look into:

1. Make sure cookies are enabled in your browser - session variables require cookies.

2. If you are using IE 6.0, the new privacy policy settings might be biting you in the rear. You will need to read

up on P3P, discover how IE is affected in Q293222 and this article, and create your own policy file(s).

3. If you are using IE 5.5 or IE 6.0, and your local server name has an underscore or other non-alphanumeric

character (other than a dash) in the name, then cookies will not work correctly. One workaround is to access

the machine by IP address; others include renaming the server or at least adding an entry to the clients'

hosts files, or alter your local DNS/WINS, or add a new host header to the web server, with a more friendly

name. For more info, see Q312461 and Q316112.

4. Make sure you haven't disabled session state in Internet Services Manager, and that it has an appropriate

timeout value:

■ Open the Internet Services Manager MMC tool, and expand 'Web Sites'

■ Right-click 'Default Web Site' or the application in question, and select 'Properties'

■ On the 'Home Directory' tab, click the 'Configuration...' button

■ On the 'Options' tab, make sure 'Enable session state' is checked and that the timeout value (in

minutes) is sufficient.

5. Check that you aren't expecting to maintain session variables across:

■ Browser windows (see Article #2172)

■ Framesets (see Q178037)

■ IIS applications

■ Nested virtual roots (see Q173307)

■ Web farms (see Q258699)

■ Protocols (e.g. http:// <-> https://, even on the same server - use a database to maintain state, as

in the cookieless shopping cart)

■ Domain names, again, even on the same server - cookies are sensitive to domain name changes
6. If you are relying on session or application variables created in global.asa, make sure that global.asa is in

fact firing (see Article #2076).

7. If you are running McAfee, it may be scanning global.asa constantly, and preventing your application from

behaving consistently (see Q303881)

8. Finally, check to see that you aren't manually disabling the user's session, either by a coding/logic/database

error, a misplaced <%@ ENABLESESSIONSTATE = False %> (which will affect a specific page), or an errant

session.timeout value (which will affect all pages).


How do I persist a shopping cart without session variables?

5,384 requests - last updated Sunday, October 1, 2000

To work properly, session variables require that cookies be enabled on the client's browser. Many people have

disabled cookies, usually after reading a Jesse Berst article where he evangelizes how cookies are the devil and

judgment day is coming soon for all those who use them.

My initial gut reaction to the initial question is always as follows:

How many people out there are seriously afraid of cookies so much that they'll disable them entirely, yet

trust your site enough to give you their credit card number online?

The discussion always goes further than that, of course. Sometimes the boss -- who always knows best -- just wants

it that way. Sometimes the programmer doesn't understand the difference between asking someone to store

temporary information on their machine and handing over their credit card information. Sometimes shopping carts

are created and then, at checkout time, there is an option for the user to fax or phone in their credit card details to

complete the transaction.

In any case, there are times where session variables would come in handy even when cookies are disabled. Many

sites have implemented a solution similar to what I'm about to describe.

You have a database table strictly for generating numbers to replace the usual session ID. Once a user has one of

these session IDs, it is appended to URLs for links and forms.

Anything they do, and any items they add to their cart, are inserted into a separate table with the session ID

alongside to identify them.

There are other ways to get around missing cookies, of course. This, IMHO, is the simplest to implement, and would

require minimal changes to your existing architecture. You would use the database structure for retrieving session

information and do away with the session variables altogether (unless you don't already get enough of the 'code two

web sites for the price of one' dilemma with Netscape and IE).

There is a sample application now available online at http://www.aspfaq.com/cart/ - you can play with the cart and

download the sample code.


Can I use IP address to uniquely identify visitors?

5,237 requests - last updated Sunday, December 10, 2000

Not reliably.

Depending on the method of the user's connection, he or she may be sharing a single IP address with dozens of

other users (as in the case of a corporate office accessing through a single server) or with thousands of other users

(as in the case of AOL's huge proxy server).

You could uniquely identify users by storing their information in a database on your side, and store a cookie on their

machine that simply stores the primary key of the table so you can look up their data easily. You could also store all

information on their machine in a cookie. Both of these solutions, of course, require that cookies are enabled (which

cannot always be relied upon).

A method I've used for maintaining state without cookies / session variables is to pass primary key information from

page to page in hidden forms. For a simple example of doing this, check out our cookieless shopping cart article. You

could easily extend this to add a username and password so people could look up their data on a successive visit.
How do I embed apostrophes (') and quotes (") in a string?

5,041 requests - last updated Sunday, December 10, 2000

Even though they're not necessary in some cases, you have several options for returning HTML with proper quotes

around filenames and parameters:

Response.write "<a href='somepage.asp'>Some Text</a>"


Response.write "<a href=""somepage.asp"">Some Text</a>"
Response.write "<a href=" & chr(34) & "somepage.asp" & chr(34) & ">Some Text</a>"

In server-side JScript/JavaScript:

Response.Write("<a href='somepage.asp'>Some Text</a>");


Response.Write("<a href=\"somepage.asp\">Some Text</a>");
Response.Write('<a href=\'somepage.asp\'>Some Text</a>');
How do I zip / unzip files from ASP?

5,019 requests - last updated Thursday, October 4, 2001

There are many components that will allow you to do this. Remember that these components are designed to allow

ASP to zip and unzip files that are on the SERVER. If you are interested in zipping / unzipping files that the client

has, these components will be no good to you unless you first upload the files to the server!

RemoteZip/ServerZip

Xceed Zip

SA-Archive

DynaZIP

WaspZip

aspEasyZIP

Zip/Unzip

I've used the last component in the list, Zbitz' Zip/Unzip. It is simple yet robust and very reliable. After much

testing, I am about to deploy it in a commercial project.

Using PKZip Command Line with Windows Script Host - sample code!

Assuming, of course, that you have Windows Script Host installed, and that you have the ability to install programs

and alter configuration on your server, you can call PKZip Command Line from Windows Script Host (which, in turn,

you can run from ASP). Here are some instructions to get you started:

a. Download PKZip Command Line from PKWare

b. Run the install for PKZip Command Line


The following steps are required in Windows 2000 and Windows XP.

c. Right-click My Computer, hit Properties

d. On the Advanced tab, click Environment Variables

e. Under 'User variables for <USER>', you will find the PATH variable

f. This PATH variable will include the PKZip folder

g. Click Edit... and copy the PKZip folder section

h. Under System Variables, double-click PATH

i. Add a semi-colon to the end of the path, and paste the PKZip folder without the quotes

j. Click OK, Apply, and OK.

Now, provided IUSR_<machine> has appropriate permissions to the destination folder, you can run a script like

this:

<%
set shell = server.createobject("WScript.Shell")
zipCommand = "pkzipc -add " & server.mappath("/") & "\test.zip c:\*.log"
shell.Run zipCommand, 1, true
set shell = nothing
%>
Why can't I pass querystring information AND links to #bookmarks?

4,904 requests - last updated Wednesday, December 13, 2000

ASP has issues with #bookmarks. These #bookmark references (and, depending on the syntax, all querystring

variables) are usually ignored when passed in combination with querystring information, e.g.:

<a href="default.asp#bookmark?id=1">GO</a>

OR

<a href="default.asp?id=1#bookmark">GO</a>

A workaround is to pass the bookmark as a querystring value, retrieve it and move to the bookmark using client-

side script. For example, in your link:

<a href="default.asp?id=1&bk=bookmark">GO</a>

Then in default.asp:

<html>
<body>
<a name="bookmark">Bookmark</a>
</body>
<%
bk = request.querystring("bk")
if bk <> "" then
%>
<script>
self.location.hash="#<%=bk%>";
</script>
<%
end if
%>
</html>

I tend to put the script at the end of the page, rather than at the beginning, since Netscape has a nasty habit of

firing JavaScript before the whole page is loaded.


Why can't I use #EXEC in an ASP page?

4,872 requests - last updated Thursday, September 14, 2000

#EXEC directives are processed by ssinc.dll, not asp.dll... therefore, this directive is only supported in .shtml files (or

files with any other extension that is mapped to ssinc.dll through IIS). Like ASP files, .shtml files can only be

processed correctly when accessed through a web server.


Why do I get 'Name redefined' errors?

4,853 requests - last updated Sunday, June 30, 2002

Microsoft VBScript runtime error '800a0411'


Name redefined: 'response'
/<file>.asp, line <line>

This error usually indicates that you have used ADOVBS.inc in two different #INCLUDE directives (possibly nested

within other includes). Another possible cause is that you have defined an identical constant in a page that also

#INCLUDEs ADOVBS.inc. You can demonstrate this with the following example:

<%
Const adOpenForwardOnly = 0
%>
<!--#INCLUDE file='ADOVBS.inc'-->

My suggestion for avoiding this, is not using ADOVBS.inc at all... it incurs a lot of overhead for a few constants. Find

out which constants you "need", copy them into your script (or your own reduced-size include), and leave

ADOVBS.inc alone. (See Article #2112 for more info.)

Another possibility is that you tried to defined a constant, giving it a name of a pre-defined object, such as the

intrinsic ASP objects. For example:

<%
Const Response = "Response message."
%>
Can I create an array's size dynamically?

4,813 requests - last updated Monday, December 2, 2002

VBScript's arrays have quite a few limitations. You may have noticed if you try this:

<%
x = 15
Dim demoArray(x)
%>

You get this error:

Microsoft VBScript compilation (0x800A0402)


Expected integer constant

To work around this, you need to declare the array without a size (or with a constant size, e.g. 0), and then re-

dimension it from the variable. Here are two examples; one using a simple array, the other a multi-dimensional

array:

<%
x = 15
Dim demoArray()
ReDim demoArray(x)
%>

<%
x = 15
y = 10
Dim demoArray()
ReDim demoArray(x,y)
%>
Note that if you want to increase the size of an array within a loop, and want to preserve the existing values

assigned to the array, you need to use the Preserve keyword (otherwise, the array gets erased and re-created, thus

destroying all your values):

<%
Dim demoArray()
for i = 1 to 5
ReDim Preserve demoArray(i)
DemoArray(i) = "test" & i
next
%>
How do I time my ASP code?

4,802 requests - last updated Tuesday, January 23, 2001

Many people have asked how they can time their ASP script, to see how efficient they are (even down to the

millisecond). Here are two techniques to do this (one has millisecond accuracy, and one can be used to derive it

indirectly).

The first method uses the timer() object, introduced in VBScript version 5.0. This returns the number of seconds

that have passed since midnight -- sounds useless, but the value contains a sparsely documented gem:

milliseconds! Yes, you heard right. So you can use script like the following to measure the number of milliseconds

that pass between the start and end of some arbitrary block of code.

<%
' get timer before task begins:

starttime = Timer()

' do some task, e.g.:

Do While z < 350000


z = z + 1
Loop

' get timer after task completes:


endtime = Timer()

' display results:


Response.Write "The task completed in " & endtime-starttime & " s"
Response.Write " (" & (endtime-starttime)*1000 & " milliseconds)."
%>

Make sure you put some substantial code in there to test that this really works (most simple tasks don't take long

enough to measure a difference; it took 350000 iterative addition calls to register 1 second of process time on my

machine). Change the number in the loop above and you will see the difference.
The second method measures the number of times a certain task can be executed within a given number of

seconds.

<%
' set number of seconds:
numSeconds = 5

' set counter:


counter = 0

' get starttime:


starttime = now()

' set endtime to some time more than numSeconds in the future:
endtime = dateadd("n", numseconds + 1, starttime)

' loop until numSeconds have passed, resetting endtime each loop:
Do Until DateDiff("s", starttime, endtime) = numSeconds
endtime = Now()
counter = counter + 1
Loop

' display results:


Response.Write "In " & numSeconds & " seconds, the task completed " & counter & " times."
Response.Write "<br>(Average time per loop: "
Response.Write FormatNumber(numSeconds*1000/counter,0,-1,0,-1) & " milliseconds.)"
%>

[Side note A: Don't forget to weigh in datetime and other calculations within your loops, they add CPU time to the

process as well. To see the difference, add the 'sometime = Now()' within the first script above, and note the

performance difference. In my tests, a simple call to Now() more than doubled the time required to finish the loop.]

[Side note B: Speed of a script is a very small percentage of the battle -- you need to test your scripts inder heavy

load and make sure the server can handle it. Because if the server goes down, Joe User isn't going to be waiting an

extra millisecond or two to get his results; he's going to be waiting until your pager goes off at 4 a.m. and you fix

the problem!]
Why can't I browse localhost without a connection?

4,751 requests - last updated Sunday, July 9, 2000

If you use a dial-up connection, IE defaults to "modem." Unfortunately, when it does this, it can't see the web server

on your own machine.

In Internet Options, on the Connections tab, change "modem" to "LAN" - the only side effect is that you can no

longer rely on IE to automatically dial your ISP for you.

Now you should be able to hit http://localhost/ or http://127.0.0.1/ or http://yourmachinename/.


How do I execute a ping command from ASP, and retrieve the results?

4,619 requests - last updated Tuesday, August 27, 2002

Here is a short code sample that should get you started.

<% Response.Buffer = true %>


<%
url = "www.espn.com"

Set objWShell = CreateObject("WScript.Shell")


Set objCmd = objWShell.Exec("ping " & url)
strPResult = objCmd.StdOut.Readall()
set objCmd = nothing: Set objWShell = nothing

strStatus = "offline"
if InStr(strPResult,"TTL=")>0 then strStatus = "online"

response.write url & " is " & strStatus


response.write ".<br>" & replace(strPResult,vbCrLf,"<br>")
%>

If you are running Windows XP or Windows.NET Server, you can use WMI's new Win32_PingStatus namespace,

e.g.:

<%
url = "www.espn.com"

WMI = "winmgmts:{impersonationLevel=impersonate}"

wqlQuery = "SELECT StatusCode FROM Win32_PingStatus WHERE Address" & _


" = '" & url & "'"

set PingResult = GetObject(WMI).ExecQuery(wqlQuery, "WQL", 48)

Response.write url & " is "


For Each result in PingResult
if clng(result.StatusCode)>0 then
response.write "offline"
else
response.write "online"
end if
Next
%>

If for some reason you can't use the shell or WMI, there are many components that can handle the task.

ASPPing

C2GPing

DesPing

DSPing Pro

DynuDNS

kutil

NETDLL

w3 Utils
How do I make my ASP page refresh?

4,033 requests - last updated Monday, July 8, 2002

You can use the Response.AddHeader method to force a timed reload or delayed refresh / redirection in an ASP

page. The following code will cycle the current page every ten seconds (and is functionally equivalent to adding a

<META REFRESH> tag to the HTML):

<%
Response.AddHeader "Refresh", "10"
%>

And the following code will redirect to http://www.aspfaq.com/ after 35 seconds:

<%
Response.AddHeader "Refresh", "35;URL=http://www.aspfaq.com/"
%>

Note that this also prevents referer information from being passed (see Article #2169 for other examples).
How do I use extensions other than .ASP for ASP files?

3,874 requests - last updated Thursday, October 26, 2000

■ Open up Internet Services Manager

■ Right-click your computer name

■ Under Master Properties, with WWW Service highlighted, click Edit...

■ On the Home Directory tab, click Configuration...

■ On the App Mappings tab, click Add

■ Using the browse button, locate asp.dll in your Inetsrv folder

■ In the extension box, type the extension you want to map to asp.dll

■ Click Apply or OK however many times is required

■ Close Internet Services Manager (if it asks you to save console settings, say yes)

■ Make an ASP file with your new extension, and navigate to it via http://servername/filename.newExtension
How do I stress test my ASP application?

3,846 requests - last updated Tuesday, November 5, 2002

The following is a list of tools and services you can evaluate. Most of the software products have evaluation versions,

and most have shortcomings. One or another may be right for you depending on your needs - some are designed to

simulate multiple users, heavy traffic from few users, and even dialup speeds.

TOOLS

DUES

Empirix e-Load

Evolvable WebTest

Innovate-IT PureLoad

Ladybug(.NET)

Microsoft's Web Application Stress Tool

OpenSTA

ParaSoft WebKing

Portent Web Load

Paessler Webserver Stress Tool

Radview WebLoad

Rational SiteLoad

RedHill Networks' WebSpray


Segue Silk Performer

SR TestWorks

Technovations' WebSizr

WebART

WebPerformance Trainer

Webserver Stress Tool

SERVICES

KeyLabs Testing Services

WebSitePulse

Other resources

Web Application Stress Tools


How do I FTP files from ASP?

3,784 requests - last updated Wednesday, June 12, 2002

[Keep in mind that the following components are used for file GET/PUT operations between the SERVER and an FTP

server. These components do not facilitate FTP operations to/from the client's system.]

ASPInet (ServerObjects.com)

DynuFTP

etiveFTP

PowerTCP FTP

Mabry's FTP/x

(I have used this control and observed performance issues. YMMV.)


Why do I get an 'overflow' error using CInt?

3,742 requests - last updated Sunday, July 9, 2000

"Integer range" is betweeen -32768 and 32767. If you expect that the number you are converting to an integer may

be outside of that range, use cLng() instead (to convert it to a Long datatype, which has a range between -

2,147,483,648 and 2,147,483,647). If your number is outside of THAT range, it probably isn't stored as a number in

the database, since the Long datatype has the same range as SQL Server 7's "INT" datatype. :-)
How do I send a MsgBox or InputBox from ASP?

3,742 requests - last updated Friday, September 20, 2002

Come on, we've all done it. Tried to send a MsgBox from ASP. And received this lovely message:

Microsoft VBScript runtime (0x800A0046)


Permission denied: 'MsgBox'
' or
Microsoft VBScript runtime (0x800A0046)
Permission denied: 'Inputbox'

MsgBox and InputBox are client-side interaction element. To work in an ASP environment, someone would have to

be sitting at the web server terminal day and night, clicking OK on every alert that comes up. Yes, I know, that's

exactly the scenario you'd like to have when debugging (and are used to with traditional client-server or stand-alone

applications). However, ASP simply doesn't allow it. Here are a few code samples that show how to use a client-side

alert (recommended for browser reach) or MsgBox. These bring up a couple of other answers too, such as how you

can nest script tags and embed single- and double-quotes in a string. Plenty of languages for everyone!

Server-Side JavaScript -> Client-Side JS Alert

<script language=JavaScript runat=Server>


var msg = "Umm, I guess, EOF?";
Response.Write("<" + "script>alert('" + msg + "');");
Response.Write("<" + "/script>");
</script>

Server-Side VBScript -> Client-Side JS Alert

<%
msg = "Umm, I guess, EOF?"
Response.Write("<" & "script>alert('" & msg & "');")
Response.Write("<" & "/script>")
%>
Server-Side JavaScript -> Client-Side VBS MsgBox

<script language=JavaScript runat=Server>


var msg = "Umm, I guess, EOF?";
Response.Write("<" + "script language=VBScript>");
Response.Write("MsgBox \"" + msg + "\"<" + "/script>");
</script>

Server-Side VBScript -> Client-Side VBS MsgBox

<%
msg = "Umm, I guess, EOF?"
Response.Write("<" & "script language=VBScript>")
Response.Write("MsgBox """ & msg & """<" & "/script>")
%>
Where can I find out about .NET?

3,711 requests - last updated Monday, January 7, 2002

aspfaq.com is not a source for learning about Visual Studio.NET and ASP.NET, at least not yet. There are, however,

several useful places for you to get more information about .NET. The most comprehensive are Microsoft's .Net

Home Page, ASP.NET Home Page, and a newly-implemented .NET Article Index ... other information is available

from independent sources:

http://www.dotnetwire.com/

http://www.devx.com/free/press/2000/vsresources.asp

http://www.asp.net/

http://www.aspnextgen.com/

http://www.aspng.com/aspng/index.aspx

And of course there are some great newsgroups covering ASP.NET on Microsoft's public news server:

microsoft.public.dotnet.framework.aspnet

microsoft.public.dotnet.framework.aspnet.webservices

microsoft.public.dotnet.languages.csharp

microsoft.public.dotnet.languages.jscript

microsoft.public.dotnet.languages.vb

microsoft.public.dotnet.languages.vc

microsoft.public.dotnet.samples

microsoft.public.dotnet.scripting
microsoft.public.dotnet.academic
How do I refresh global.asa without restarting the application?

3,596 requests - last updated Sunday, May 27, 2001

Plenty of people have asked how they can manually force global.asa to fire, without stopping and starting the web

server or application. They already know they can't hit global.asa directly with a browser; this ends up in a 500-15

HTTP Error (Requests for global.asa not allowed).

There is a way around this. This example will deal with forcing the application_onStart() routines to fire, without

disrupting the server.

The trick is to keep the logic in an #include file, and store that as an ASP page you can hit manually. Yes, many

people aren't aware that you can #include a file in global.asa, but you certainly can. Here is some syntax within

global.asa:

<script language=vbscript runat=server>


sub application_onStart()
</script>
<!--#include virtual='/admin/start-app.asp'-->
<script language=vbscript runat=server>
end sub
</script>

The trick is that both global.asa and the #include file must have matching script types... no <%%> delimiters. The

ASP page should also include straight script (no subs or functions) and not use any response-related methods. So

your start-app.asp file should look like this:

<script language=vbscript runat=server>


' do stuff
' insert into database
' set application variables
' etc etc.
</script>

Now this code will fire in the application_onStart() event, but you can also override it by hitting
http://yourserver/admin/start-app.asp as well.

This is very handy; however, I first employed this technique back during one of the first global.asa-related security

exploits.
How do I protect my client-side JavaScript code?

3,341 requests - last updated Tuesday, October 30, 2001

This has been asked thousands of times over the past few years. Everyone wants to know how they can prevent

nosy people from viewing or stealing their JavaScript. I have always responded with:

"If my browser can read it, so can I."

"If you don't want people to steal your JavaScript, don't put it on the web."

I have also often thought the following:

"If your Javascript is so revolutionary, you should probably be able to figure this out too."

"And if someone does steal your script, consider it a compliment."

In the past, many people have suggested ways to slow people down, and discourage casual, non-persistent people.

But there has never been a way to stop anyone with a little more resourcefulness than your average house fly. <G>

Some of the solutions that have been offered in the past, and their workarounds:

Proposed fix Workaround(s)

Load the JS in a new window, with toolbar disabled - view source by right-clicking within the window

Disable right-click by capturing oncontextmenu - disable Javascript temporarily

- use a browser that doesn't support oncontextmenu

- load 'view-source:<url>'

Use JavaScript across frames, iframes, ilayers - not difficult to find the proper page

Use JavaScript to write JavaScript - disable JavaScript temporarily

'Hide' the script using a remote JS file - the .js file is in your cache

- Microsoft's encoder was broken easily; perhaps you can


Encode the script using an encoder
write a better one
- all this does is obfuscate, it doesn't make it hard to copy at
Obfuscate with unSpace or similar tools
all

- a resourceful person will still be able to see your script,


All of the above
even if you employed all of the above at once

Other legitimate ways to view JavaScript sent to the browser is by using packet sniffing technology,

Here is a funny excerpt from a post by Jeff Cochran, back in 1997:

Put the source on a floppy disk and bury it in a mason jar in your back yard. Make sure you delete

all references to it from your system. To be extra sure, bury it blindfolded at night so you can't

remember where it is in case foreign spies try to beat the location out of you.

Of course, you could write it as a Java Applet to make it tougher to figure out, but then, if you knew

how to do that you'd probably also be able to write something really worth protecting...

Jeff

PS: The mason jar trick works well to protect your .GIF files from being downloaded too...

UPDATE October 30, 2001

After many attempts at hiding my JavaScript code, I was thwarted every single time - by people using tactics

ranging from intuitive to downright clever.

Once again, I have to tell you... if you need to hide your JavaScript code, you probably shouldn't put it on the web.
How do I access all active sessions on the server?

3,196 requests - last updated Tuesday, January 23, 2001

You can't access one user's session from another session (even if you are physically at the machine). However, you

can follow our cart example to some extent, and store all the session data in a database. Keep a column called

'active' which indicates whether the user is still there. When the user logs out (or after a specified time period, which

you can schedule using a job in SQL Server), you turn the active flag to off. Passing a single integer session variable

around, and querying the database when you need to, is much more efficient and scalable than storing all those

values in session variables anyway (even if you need all the values on every single page!). I just finished re-working

a very major application to handle things this way (partly for performance, and partly because the session variables

were becoming unruly and unpredictable).

Remember, don't use sessionID as a key, and expect to uniquely track users (see Article #2085 for more info).
What is this error 'An unhandled data type was encountered'?

3,067 requests - last updated Sunday, August 11, 2002

This usually happens when you mix up names of string/numeric variables and array variables. For example:

<%
f = "hello"
'...
f = split("foo","bar")
'...
response.write(f)
%>

Will produce this error:

Response object error 'ASP 0106 : 80020005'


Type Mismatch
/<file>.asp, line 0
An unhandled data type was encountered.

To get rid of this error, check the line it occurs on for any variables used, then scan through the file for any other

use of that same variable name. If the error returns line 0, you will have to do a bit more digging through your file --

you can narrow your search down to look at the names you give complex data types (e.g. objects or, more

commonly, arrays).
Should I use sessionID to uniquely identify users (e.g. primary key)?

3,045 requests - last updated Saturday, January 20, 2001

NO. SessionID is a "random" string and, as noted in the documentation for all versions of IIS, can be repeated. So if

you store information for a user based on the SessionID value, be very aware that a new person next week might

happen to get the same SessionID value -- this will either violate a primary key constraint, or mix two or more

people's data.
Why won't QueryString values work with Server.Execute?

3,043 requests - last updated Sunday, August 6, 2000

IIS 5.0's Server.Execute command is fairly useful. However, as many people have pointed out, if you use a

QueryString value, you receive this error:

Invalid URL form or fully-qualified absolute URL was used. Use relative URLs.

Even when your URL *IS* relative (removing the QueryString value makes the page function properly).

Unfortunately, Microsoft has yet to recognize this officially as a bug. So in the meantime, you must rely on session

variables or database entries to retrieve any information you would like to pass to the target page.
Can I run IIS 5.0 / ASP 3.0 on Windows NT 4.0 or Windows 9x?

3,034 requests - last updated Tuesday, October 30, 2001

No. See Article #2075 for information on obtaining web server products for these operating systems.
Why does DLLHOST.EXE take all my memory and/or CPU?

2,876 requests - last updated Tuesday, August 20, 2002

COM Objects

Since DLLHOST.EXE keeps references to your COM objects, it may be that these objects have memory leaks. Pore through your

custom objects and make sure you release all references. If you are using C++/ATL, make sure to use "." as opposed to "->" when

destroying pointers. If you are using Visual Basic, make sure that you have read Q264957, Q281630 and that you have set Retain

in Memory and Unattended Execution. If you are using a third party vendor's object, check their web site to see if they have

released any patches to fix memory leaks.

If you are uploading large files with ASPUpload, for example, see PS01041741 for suggested fixes.

Data Access Components

Sometimes this can be caused by MDAC components - either by poor implementation, a faulty install, or a true memory leak in the

component. Eliminate the possibilities by getting the most recent version from http://www.microsoft.com/data/.

ASPTrackThreadingModel

One reason could be that your Metabase has a setting of 0 for ASPTrackThreadingModel. For optimal performance, in IIS 4.0, this

setting should be 1. If you are running IIS 5.0, you shouldn't adjust this setting.

To verify / solve this issue, download the Metabase editor (MetaEdit 2.2) from Q232968.

Once it is installed:

- Launch the editor (from the Administrative Tools menu).

- Expand 'Schema'

- Expand 'Properties'

- Highlight 'Defaults' and scroll for ASPTrackThreadingModel

- Double-click that key

- if Data says 0, change it to 1, click OK, close the editor, reboot.


Too Many Applications

Perhaps you simply expect one server's resources to handle too many web sites / applications.

Bonehead Mistakes

Yes, we've all made these. Before you go opening a ticket with Microsoft, make sure your code doesn't look like this:

<%
Dim i
i = 1
Do While i < 10
Response.Write "I'm going to steal your memory."
Loop
%>

Other examples are very large loops (e.g. for i = 1 to 10000000000), forgetting to close / set objects to nothing, circular

references, forgetting rs.movenext within a do while not rs.eof / loop construct, recursive functions, returning massive amounts of

data from SQL Server or Access to an ASP page, storing ADO objects in the session or application objects...
Still haven't solved it?

Well, you could use performance monitor to at least narrow down the application(s) causing the problem. Under Internet Services

Manager, try changing the application protection under Internet Services Manager to High (isolated). Right-click the Default Web

Site (or the troublesome application), choose Properties, and on the Home Directory tab, change the application protection option.

Hit Apply, OK, exit ISM and try and reproduce the problem. This may take some experimentation.

If you have several COM objects and you can't narrow down which is causing the problem, put each in its own application, and wail

on each one (in a realistic environment) until you reproduce the problem.

See Q253706 for more information on tracking down memory leaks.


How do I generate unique GUIDs from ASP?

2,821 requests - last updated Sunday, October 28, 2001

There is a free component that will help you do this; it is called GUIDMaker and is available at ServerObjects Inc.

If you have access to SQL Server from your ASP page, you can call the following script:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("SELECT newid()")
Response.Write rs(0)
' ...
%>

Or from ASP, you can use this:

<%
Function GetGuid()
Set TypeLib = Server.CreateObject("Scriptlet.TypeLib")
GetGuid = TypeLib.Guid
Set TypeLib = Nothing
End Function
Response.Write GetGuid()
%>
What is this 'Cannot detect OS type' error with NT 4.0 Option Pack?

2,785 requests - last updated Monday, September 18, 2000

From KB article Q230287:

When installing IIS 4.0 on a computer running Windows NT Server, the NetLogon and Computer Browser services

must be running. If these services are not running, you will receive a dialog that says "Cannot detect OS type" and

Setup will fail.

See also Q205328 and Q250262


How do I solve 'The server failed to load the application' errors?

2,724 requests - last updated Tuesday, April 17, 2001

Several people have reported error number 8007053D recently. There is a KB article that goes into depth about it

(and how to fix it):

Q238665

There is another that applies to similar W3SVC errors that might appear in your event log.

Q200514
Can I host multiple sites in Workstation or Professional?

2,662 requests - last updated Tuesday, October 30, 2001

No. These operating systems have some limitations compared to their server counterparts. One is the way that PWS

(NT 4.0) and IIS (Windows 2000, Windows XP Pro) are configured... two of the biggest obstacles people find with

the lower-grade OS is that client connections are limited to 10, and that you can only host one web site. You also

cannot configure the HTTP service's port from its default of 80; this means anyone on a cable network (e.g. @home,

RoadRunner) that has shut off port 80, is going to have to bite the bullet and upgrade (or switch providers).
How do I get the lbound/ubound of the nth element in a multi-dimensional array?

2,661 requests - last updated Sunday, December 10, 2000

This one is a tricky one, and displays another weakness in VBScript's array object. While most things in VBScript are

0-based, the "collection" of elements in a multi-dimensional array is actually 1-based. The default element for

lbound/ubound values (these give the lowest and highest numbers in the array) is the first element. So, when you

have a multi-dimensional array, the following commands do the exact same thing:

<%
dim demoArray(10,20)
Response.Write ubound(demoArray)
Response.Write ubound(demoArray,1)
%>

The ,1 indicates that you want the ubound for the FIRST element in the array. So, extending that, here is how to get

the lbound/ubound for other elements in the array:

<%
dim demoArray(10,20,30)
Response.Write ubound(demoArray,2)
Response.Write ubound(demoArray,3)
%>
Is there an easier way to patch my server(s)?

2,540 requests - last updated Saturday, December 14, 2002

For information on obtaining the lastest service packs for Windows 2000, see Q260910 (Windows 2000).

For information on obtaining service packs for Windows XP, see Q322389. Everyone running Windows XP should

not be without Service Pack 1 (see a list of fixes here - this page is linked to from Q324720 and release notes

are listed in Q324722). You can also see a subset of the fixes, namely the security-related updates, at this

Technet page.

For information on obtaining the latest service packs for SQL Server, see Q290211 (SQL Server 2000) and

Q301511 (SQL Server 7.0).

For the latest updates to the .NET Framework, see

http://msdn.microsoft.com/netframework/downloads/updates/sp/default.asp.

For other hotfixes, organized by technology, see http://support.microsoft.com/?scid=FH;%5bLN%5d;hotfixes.

Recent security updates and other information:

December 11, 2002

■ There is a patch involving WM_Timer in Security bulletin MS02-071 and Q328310.

■ Due to a flaw in SMB Signing, there is an update available in Security bulletin MS02-070 and Q309376.

■ And likely most important in this bunch, there is a critical update to Microsoft's Virtual Machine - if you are

running the VM, please read Security bulletin MS02-069 and Q810030. You can obtain this patch through

Windows Update.

December 4, 2002

■ There is a patch available for Internet Explorer. For more information, see Security bulletin MS02-068 and

Q324929.
■ There is a patch available for Outlook XP / 2002. For more information, see Security bulletin MS02-067 and

Q331866.

November 20, 2002

■ Microsoft released an MDAC patch for a potential buffer overrun exploit. If you haven't upgraded to MDAC

2.7 yet (http://www.microsoft.com/data/), please do so, after applying this patch. For more information, see

Security bulletin MS02-065 and Q329414.

■ There is a cumulative patch available for Internet Explorer 6.0. See Security bulletin MS02-066 and

Q328970 for details and a download.

October 30, 2002

■ Cumulative patches for IIS 4.0, 5.0 and 5.1 has been released. This patch is recommended for all systems...

please see Security bulletin MS02-062 and Q327696.

■ MS released a fix for a potential unchecked buffer exploit on Windows 2000 and Windows XP systems. See

Security bulletin MS02-063 and Q329834.

■ They also released a fix for a potential Windows 2000 trojan horse exploit; see Security bulletin MS02-064

and Q327522.

■ A cumulative patch for Outlook Express hits the web. The most notable fix here is that OE finally has a

delete button that is usable within newsgroups (previously we had to use bizarre and tedious workarounds to

prevent, for example, a post-dater's message from appearing at the top of our message list for weeks). See

Q331923 for more details and a download link.

October 16, 2002 - SQL Server users should install the cumulative patch from Security bulletin MS02-061 and note

that the related KB articles (Q316333 for SQL Server 2000, and Q327068 for SQL Server 7.0) have been updated

yet again.

October 10, 2002 - If you are running Windows XP or have Outlook Express 5.5 or 6.0, see Security bulletin MS02-

058 and Q328676.


October 2, 2002 - If you are using Services for Unix, see Security bulletin MS02-057 and Q329209.

October 2, 2002 - All Windows users should review Security bulletin MS02-055 and Q323255.

October 2, 2002 - Those running Windows 98, ME or XP should review Security bulletin MS02-054 and Q329048.

September 25, 2002 - If you are running FrontPage Server Extensions, review Security bulletin MS02-053 and

Q324096.

September 18, 2002 - Please update your system(s) with the JVM patch from Security bulletin MS02-052 (and see

Q329077 for more details. If you are running Terminal Services or Remote Desktop, please see Security bulletin

MS02-051 and Q324380.

September 13, 2002 - Anyone running Microsoft Word and concerned about recent security problems should read

this reponse.

September 9, 2002 - For those of you running Windows XP, you should get SP1 as soon as possible. If you can't,

one thing you should consider doing is renaming %WINDIR%\PCHEALTH\HELPCTR\System\DFS\uplddrvinfo.htm

(see Q328940 for more information) -- however this won't completely solve the problem. For more information, see

this explanation.

September 5, 2002 - Microsoft updated Security bulletin MS02-050 and Q328145 / Q328691, outlining a critical

patch for all Windows and Mac users.

September 4, 2002 - Security bulletin MS02-049 and Q326568 describe a potential exploit for Visual FoxPro users.

August 28, 2002 - Microsoft released a patch for a new exploit involving digital certificates. See Security bulleyin

MS02-048 and Q323172.

August 22, 2002 - Microsoft released a patch for a new denial of service exploit, in Security bulletin MS02-045 /

Q326830 and a new version of the TSAC ActiveX control in Security bulletin MS02-046 / Q327521. In addition, they

released a cumulative patch for Internet Explorer, which should be applied to, at minimum, development

workstations. This patch is made available in Q323759 and described in Security bulletin MS02-047.

August 21, 2002 - If you are running Project Server, Commerce Server, BackOffice Server, ISA Server, or BizTalk

Server, and/or are using Office Web Components, you should see Security bulletin MS02-044, which fixes a few
potential security issues (as explained in Q322382 and Q328130).

August 7, 2002 - Microsoft released a patch for Content Management Server 2001 in Security bulleting MS02-041

(for Q326075).

August 1, 2002 - Microsoft released Windows 2000 Service Pack 3. This release fixes many security issues and is a

recommended upgrade (as outlined in Q320853 and this long URL).

July 31, 2002 - Microsoft released a patch for MDAC 2.5 through 2.7 in Security bulletin MS02-040 (for Q326573).

July 24, 2002 - If your server is running SQL Server 2000, you'll want to install the Q316333 Cumulative Patch and

then the patch from Security bulletin MS02-039 (for Q323875).

June 26, 2002 - Microsoft released Security bulletin MS02-033, which is applicable to those of you running

Commerce Server (Q322273).

June 12, 2002 - Microsoft released Security bulletin MS02-028, which deals with HTR requests (Q321599).

June 6, 2002 - Microsoft released Security bulletin MS02-026, which involves the ASP.NET worker process

(Q322289).

April 29, 2002 - Microsoft updated Q294370 with a few new fixes for specific invalid requests.

April 10, 2002 - Microsoft released Secutiry bulletin MS02-018, outlining a cumulative patch applicable to NT 4.0 /

IIS 4.0, Windows 2000 / IIS 5.0, and Windows XP / IIS 5.1 - details are available in article Q319733.

And you can get version 2.5 of the URLScan utility (updated April 12, 2002):

Q307608

Previous patches

On January 30th, Microsoft made a cumulative patch for Windows 2000 / IIS 5.0, encompassing all post-SP2
security issues. You can download the patch, and read more about it, at the following page:

Windows 2000 Security Rollup Package, January, 2002

If you are running NT 4.0, then you can download the May 14th cumulative path which fixes all security issues from

NT 4.0 Service Pack 5 onward. This patch should be much easier to apply than combing through the many individual

patches that have been available prior to May 14th.

Here is where you can download the patch and get more information:

Microsoft Security Bulletin MS01-026

And on November 14, 2001, Microsoft issued version 2.1 of the IIS Lockdown Tool:

IIS Lockdown Tool

You should also watch the following URLs for updates, some of which may affect your environment:

Security Bulletin Search

Windows 2000 Post-SP2 Hotfixes

Further, you can download the Security Hotfix Checker, allowing you to determine which machine(s) on your

network are missing security patches. The download is found in Q303215 and Q & A is found in Q305385.
What kind of object is Response.Crackers?

2,500 requests - last updated Tuesday, November 13, 2001

I'm sorry if you're out there Mike, but I had to exploit this one a bit and have some fun with it (at your expense). On

November 12th, Mike posted the following after tirelessly searching for information about "crackers":

OK, go to www.aspfaq.com, and search for "cookies."

I got 5 hits, the second was #2058 "How do I check for enabled cookies..."

That page has two examples, cookietest and cookietest2:

if request.Crackers("enabled")="1" then
response.write("cookies are enabled")
else
response.write("cookies are not enabled")
end if

Yet if you search for the term "crackers," nothing is found.

As it turns out, Mike is running cookie-blocking software called Proxomitron -- if you have this software installed,

and you are viewing code samples on the web, you might be surprised to see references to objects like

'Request.Crackers' and 'Response.Crackers'. Fact is, this software is simply intercepting the HTML that comes into

your browser, and replacing all instances of '.cookies' with '.crackers' ...

So, if you go to Oreo.com, you might be a little confused when you see the following recipe:

SURPRISE OREO TUNNEL CAKE


The tunnel inside this angel food cake is filled with chocolate pudding, finished with Oreo
crackers and chilled to create a surprising dessert course.

Of course, this won't happen, but I wanted to embellish the story a little bit. Some people were a little mean, and

called Mike "crackers" in response. A little confused, and understandably so, but not crackers. In any case, it was

quite funny -- and I hope nobody is disgruntled about me sharing it here. After all, it could have happened to you.
How do I embed ASP delimiters (<% or %>) in a string?

2,442 requests - last updated Wednesday, September 4, 2002

Many people get choked up when trying code similar to the following:

<%
Response.Write "<table width=100%>"
%>

This will result in the following error:

Microsoft VBScript compilation (0x800A0409)


Unterminated string constant

This is because the ASP engine tries to parse all the <%%> blocks first, and ...width=100%> looks like the end of

an ASP block (which of course isn't the case). There are several ways to work around this; for example, you can use

valid HTML by enclosing the width attribute's value in quotes, you can leave a space after the percent sign, you can

organize your element tag in a different order, or you can escape the sequence using a backslash.

To wit:

<%
response.write "<table border=0 width='100%'>"
response.write "<table border=0 width=""100%"">"
response.write "<table border=0 width=" & chr(34) & "100%" & chr(34) & ">"
response.write "<table border=0 width=100% >"
response.write "<table border=0 width=100%\>"
response.write "<table width=100% border=0>"
%>

For simplicity's sake, I prefer the first method - partly because it's much easier to read, and partly because any

HTML attribute that isn't a number should be enclosed in quotes of some kind.
This is a trivial example, of course; but the same principals apply when creating an ASP file using FileSystemObject.
Can I dictate the load order of files on the client from ASP?

2,360 requests - last updated Tuesday, January 23, 2001

Some people have asked whether or not they can specify that certain HTML elements (such as images, Flash,

applets etc.) load before others in an HTML page.

While there are certainly ways to handle this with client-side code (DHTML and JavaScript), the answer is NO. ASP

runs on the server, and simply returns HTML to the client. That's it.
How do I comment blocks of ASP code?

2,308 requests - last updated Sunday, June 30, 2002

Many people get frustrated because VBScript doesn't allow you to comment blocks of code; instead, a REM or ' is

required before every line. Other languages, such as Java, JavaScript, Perl, and T-SQL allow the use of delimiters

such as /* comments */ which allow you to comment single or multiple lines without much fuss. So is there a way to

do this in VBScript as well?

Yes! Here are two ways of preventing a block of code from writing... the first requires two comments to make it run

/ not run, the second requires one line change.

<%
Sub doNotExecuteThisCode
For Each x In Request.Form
Response.Write x & "<br>"
Next
Response.End
End Sub
%>

Now, to execute the code again, simply comment the Sub and End Sub lines:

<%
'Sub doNotExecuteThisCode
For Each x In Request.Form
Response.Write x & "<br>"
Next
Response.End
'End Sub
%>

The following method uses simple logic to enter a code block.


<%
ExecuteCodeBlock = FALSE
If ExecuteCodeBlock Then
For Each x In Request.Form
Response.Write x & "<br>"
Next
Response.End
End If
%>

Now, to execute the code again, simply change the value of ExecuteCodeBlock to TRUE:

<%
ExecuteCodeBlock = TRUE
If ExecuteCodeBlock Then
For Each x In Request.Form
Response.Write x & "<br>"
Next
Response.End
End If
%>

FWIW, the latter method is a little less efficient, since the If logic is actually tested. I use it all the time, however,

since it's much easier to set a debug flag on or off to affect a group of blocks of code.

Another option is to use Visual Studio.NET's new multi-line comment functionality:

To make this command visible, the 'Text Editor' toolbar must be checked under Tools / Customize... / Toolbars.
Where can I find out about running Perl in IIS?

2,240 requests - last updated Tuesday, July 11, 2000

ActiveState has helpful documentation and downloads that will help get you up and running with Perl in no time.
How do I warn people when their session is about to expire?

2,057 requests - last updated Sunday, March 10, 2002

Many people want to use response.redirect in Session_OnEnd() to trap the session event (whether to give the user a

prettier message than telling them they're not logged in, or to clean up database activity, etc). As explained in

Article #2078, Session_OnEnd() is unreliable at best.

Here's one idea. Use client-side script to send the user a warning just before the timeout. The following example,

used on each page, demonstrates how to warn the user two minutes before their session will expire. You are free,

obviously, to change the code that happens within the JavaScript (e.g. call a function), and to change the

advanceWarning value.

<%
advanceWarning = 2
jsTimeout = (session.timeout - advanceWarning) * 60000
%>
<script>
window.setTimeout("alert('Session is about to expire');",<%=jsTimeout%>);
</script>

This example assumes the page is sitting idle, and doesn't have any components (FRAMEs, parent documents,

IFRAMEs, IMGs) that are interacting with the server. In those cases, users might be warned far earlier than their

session will actually time out.

You might be careful if you use this kind of code (say at exactly the session timeout, rather than two minutes

before) to clean up database or other session data by calling a separate ASP file from JavaScript (e.g. in a hidden

IFRAME, new window, or even by changing the SRC attribute of an IMG). The JavaScript code will only fire if the

browser window is still open (also a limitation of Session_OnEnd()).


How do I determine which version of IIS / ASP I'm running?

1,768 requests - last updated Sunday, May 20, 2001

There are a few answers to this question.

1. If you are running Windows 2000, you are running IIS 5.0 / ASP 3.0.

2. If you are running Windows XP, you are running IIS 5.1 / ASP 3.0.

3. If you are running Windows NT 4.0 or Windows 9x, you can determine which version of IIS / PWS you are

running by one of the following methods:

<%
response.write(Request.ServerVariables("SERVER_SOFTWARE"))
' returns "Microsoft-IIS/4.0" for IIS 4.0 + ASP 2.0
%>

OR

Look for Windows NT 4.0 Option Pack in Add/Remove Programs (Control Panel). If it is there, you

are running IIS 4.0 (NT Server) or PWS 4.0 (NT Workstation or Win9x).

OR

A. Do a search for ASP.DLL on your system

B. right-click it and select Properties

C. Check the version tab:

IIS 3.0 / PWS 1.0x should show 1.x

IIS 4.0 / PWS 4.0 should show 2.x

IIS 5.0 should show 5.0.2195.x


How do I turn a KB Article #, like Q191987, into a usable URL?

1,655 requests - last updated Monday, December 2, 2002

So, some schmuck in a newsgroup told you to go see Q191987, and now you're wondering how to get there?

DUH! That's simple, it's http://support.microsoft.com/?kbid=191987 ... okay, that DUH was funnier back when the

KB was in a more cryptic format, with slashes inserted at various intervals in the middle of the 6-digit article

number.

If you have MSDN Library installed, you can open it and search for the Qxxxxxx string. (If you don't have an MSDN

Library subscription, I highly recommend getting one. They're relatively cheap, and easily outperform searching the

online versions of the KB and MSDN -- by about 50 times.) You can also search Google for the Qxxxxxx string; you

will often find a cached version of the article, and Google's database is known to be more reliable than the

knowledge base (currently there is a large flux of articles that are "in migration" -- in other words, missing). You can

also switch to the groups interface to see discussions from newsgroups involving the article in question -- pretty

neat feature.

The following URL format is still valid, but will stop working at some point -- so you should consider migrating away

from it (particularly if you store these URLs in your own knowledge base or web site).

http://support.microsoft.com/?scid=kb;en-us;Q191987

We actually store only the KB article, and the formatting is handled at generation time. This way, when Microsoft

changes their URL format (which they do often), we only have to change one line of code and the entire FAQ is

immediately updated.

Not so hard, right? Now, to do this bit at will, copy the following piece of code and save it in a local HTML file,

perhaps sitting on your desktop ready to launch:


<form name=a onsubmit='return false'>
<input type=text name=kb>
<input type=button onclick='go();' value=' Go '>
</form>
<script>
function go()
{
var kb = document.a.kb.value;
var url='http://support.microsoft.com/?kbid='
if (!isNaN(kb))
{
window.open(url+kb,'','');
}
}
</script>

A fellow MVP (Alex Angelopoulos) has created HTA files for this same purpose, e.g. to search Google for specific KB

articles:

http://dev.remotenetworktechnology.com/mvp/googles.zip
How do I make the search engines index my ASP pages with QueryStrings?

1,613 requests - last updated Tuesday, July 9, 2002

As you may know, most of the popular search engines -- for various reasons -- do not index pages that have

querystring parameters. So, what site owners must resort to in order to get into the search engines is to make static

copies of their dynamic data.

While you can do this yourself manually (e.g. write an admin interface that uses FileSystemObject to re-write HTML

files when data changes), there are ISAPI filters out there that will help you index with the search engines and still

keep all of your content fresh. Here are a few that I have come across:

ASPSpiderBait

IISRewrite

ISAPI_Rewrite

URLReplacer

XCache

XQASP

If you know of any other tools, please let us know...


How do I host multiple web sites on one IIS box?

1,612 requests - last updated Monday, February 26, 2001

Check out this resource at IISFAQ.com, and this one at IISAnswers.com


How do I set session variables from client-side script?

1,585 requests - last updated Friday, June 14, 2002

This code currently assumes IE as the browser... I will work on porting the code to work in Netscape. But it does

demonstrate that it is possible to create / change session variables from script.

The Session variable "MyVar" contains: <%=Session("MyVar")%>


<p>Update it to <input type=text ID="txtSession"
size=50 value="<%=Now()%>">
<p>
<input type=button value="Set Session var via hidden image"
onClick="SetSessionIMG()"><br>
<input type=button value="Set Session var via hidden IFRAME"
onClick="SetSessionIFRAME()"><br>
<input type=button value="Set Session var via XMLHTTP"
onClick="SetSessionXMLHTTP()"><br>
</p>

<p><a href="ClientSession.asp?random=<%=server.URLEncode(Now())%>">Refresh
this page</a>

<img src="" ID="imgDummy" width="0" height="0"


style="display:none;">
<iframe src="" ID="iframeDummy" width="0" height="0"
style="display:none;"></iframe>

<script language="VBScript">
sub SetSessionIMG
sValue = trim(document.all("txtSession").value)
set objImg = document.all("imgDummy")
objImg.src = "SetValue.asp?SessionVar=" & Escape(sValue)
end sub

sub SetSessionIFRAME
sValue = trim(document.all("txtSession").value)
set objIFrame = document.all("iframeDummy")
objIFrame.src = "SetValue.asp?SessionVar=" & Escape(sValue)
end sub

sub SetSessionXMLHTTP
sValue = trim(document.all("txtSession").value)
set obj = CreateObject("Microsoft.XMLHTTP")
obj.Open "GET", "SetValue.asp?SessionVar=" & Escape(sValue), False
obj.Send
end sub
</script>

The code in SetValue.asp would be fairly simple:

<%
Session("myVar") = Request.QueryString("SessionVar")
%>
Why do I get 800A0414 errors?

1,539 requests - last updated Monday, August 19, 2002

If you are calling a simple subroutine, or a function that has no return value, you omitted the CALL keyword before

the function/sub call, or added parentheses. For example, the following code:

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CopyFile("c:\test.txt", "c:\test.bak")
set fso = nothing
%>

Produces the following error:

Error Type:
Microsoft VBScript compilation (0x800A0414)
Cannot use parentheses when calling a Sub
/<file>.asp, line <line>, column <column>
fso.CopyFile("c:\test.txt", "c:\test.bak")
-----------------------------------------^

The solution, according to MSDN:

■ Remove the parentheses from the subroutine invocation.

■ Use the Call statement to invoke the subroutine instead.

So, you would re-write the above code as follows:

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
fso.CopyFile "c:\test.txt", "c:\test.bak"
set fso = nothing
%>
or

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
CALL fso.CopyFile("c:\test.txt", "c:\test.bak")
set fso = nothing
%>
How do I round a number *properly* with VBScript?

1,522 requests - last updated Sunday, April 1, 2001

Admittedly, VBScript's built-in round function leaves a lot to be desired. Thanks to Anthony Sullivan and Mike Trinder

for helping me to build the following function, which seems to work a lot better in all cases:

<%
Function roundit(number,decPoints)
decPoints = 10^decPoints
roundit = round(number*decPoints+0.1)/decPoints
End Function
%>
How do I know which version of VBScript my server is running?

1,362 requests - last updated Monday, May 7, 2001

This should do the trick:

<%
response.write "VBScript Engine: "
response.write ScriptEngineMajorVersion
response.write "."
response.write ScriptEngineMinorVersion
%>

If you have direct access to the machine (either physically or through remote control software), you can do one of

the following:

1. Use Windows Scripting Host, if it is installed. Save the following script as .vbs and double-click it:

msgbox ScriptEngineMajorVersion & "." & ScriptEngineMinorVersion

2. Use client-side VBScript. Save the following as .html, and open with Internet Explorer:

<script language='vbscript'>
msgbox ScriptEngineMajorVersion & "." & ScriptEngineMinorVersion
</script>

3. Finally, you could right-click vbscript.dll, which should be in %windir%\system32\, choose Properties,

and look at the Version tab.


How do I make my ASP pages more efficient?

1,343 requests - last updated Monday, November 25, 2002

General | Database-related

■ If possible, put web root, temp, database, system and pagefile on separate partitions. If separate physical

drives, even better. If you can use RAID 5 or RAID 0 + 1, by all means, do so. Keep your drives

defragmented, and make sure you have a patched system.

■ Don't generate large strings in single variables -- concatenation is very expensive. If you are looping through

an array or recordset and building a string, only to dump it to the screen at the end of the loop, use

response.write within the loop instead. Time it, you might be surprised how inefficient concatenation can be

(and this is exponential as the size of the string goes up). If you are looping through and building a string

that will need to be used in multiple places later on, consider an array of smaller strings. VBScript's string

buffer is only so large.

■ Avoid storing too much data in session variables, and also make sure your session.timeout is reasonable.

This can use up significant amounts of memory on the serevr, and session variables hang out long after the

user closes the browser. In my testing, scenarios where large amounts of session variables were used,

performance was enhanced by replacing this with a single session variable (e.g. an IDENTITY value, or a

GUID()) used to store / retrieve "session" data in a database. See our cookieless shopping cart for

somewhat of an example. And of course, just about every object you will use in ASP should not be stored in

session or application scope (see Article #2053).

■ Try not to do too much work, especially interaction with a database or remote server(s), in a single script.

Consider optimizing the code and perhaps spreading the work over multiple pages. I often see errors like

this happening:

<some error>
/file.asp, line 1294

This seems to be *way* too many lines of ASP code to (a) manage and (b) expect to run efficiently. I've

worked on some pretty big ASP applications and I don't recall ever having an ASP script more than 250 lines

long.
■ Try to avoid nested loops of any kind.

■ Use Response.Buffer = true.

■ Use Option Explicit. Yes, this can be a pain in the butt and slow down development slightly, but it forces you

to use locally declared variables. As Eric Lippert [MS] explains in this Google post, using declared variables is

more efficient. (As an aside, Option Explicit helps prevent seemingly simple typographical errors, which

always turn out to be a big production because you can't figure out why myNumber is blank, when in

actuality you spelled the initial declaration myNunber.)

■ Compare apples to apples! Instead of making the ASP engine implicitly cast values for you, force it. Also,

instead of this:

if clng(rs("whatever")) = 1 or clng(rs("whatever")) = 6 then

Store this variable locally... both to avoid multiple cast operations, and to avoid looking up the value from

the rs object multiple times. So it would become:

whatever = clng(rs("whatever"))
if whatever = 1 or whatever = 6 then

■ Use readall() instead of while not AtEndOfStream / readLine. I've seen this kind of logic far too often:

do while not fs.AtEndOfStream


localLine = fs.readLine()
wholeString = wholeString & vbCrLf & localLine
loop

The following is much more efficient. You get the whole string in one call, and this allows you to close the

handle on the file immediately, and do the processing afterward:


wholeString = fs.readAll()

■ Use server-side validation, as opposed to generating large client-side

strings / arrays, to prevent duplicates and other problems in forms. You may recall in SAMS' Active Server

Pages 2.0 Unleashed, I wrote a section about using data from the server in client-side validation, to prevent

duplicates and such WITHOUT requiring a round-trip. But this is only useful with smallish data sets. The

client side can only handle so much JavaScript before it croaks. The Mac will crash and burn far earlier than

the PC. I haven't seen this cause problems per se on the PC, only sluggishness.

■ There are some other general recommendations at the following URLs:

25+ ASP Tips to Improve Performance and Style

Improving ASP Application Performance

ASP Guidelines

Database-specific | General

■ Make sure you're using MDAC 2.7 (see Q300420 for one of the problems with 2.6) and the most recent SQL

Server service pack. For information on keeping your server secure and healthy, see Article #2151.

■ I am currently using many of the suggestions described in the SQL Server 2000 Operations Guide. If you're

using SQL Server 2000, please take a look at this excellent article.

■ Use a DSN-less connection string (see Article #2126), and enforce TCP/IP (avoiding name resolution) by

using Network=DBMSSOCN in the connection string (see Article #2082). Use ADO and OLEDB, not DAO or

ODBC. Use the EXACT same connection string throughout your application, in order to make the best

possible use of connection pooling.

■ Open your connection just before needing it, and close it as soon as you're done with it. Your motto should

always be "get in, get/save data, get out."

■ Avoid adovbs.inc (see Article #2112). If you absolutely must use named constants, define the subset you

need, or use the reference to the MDAC type library in global.asa.


■ Avoid storing recordsets or connections in session or application variables (see Article #2053).

■ If your SQL queries take too long to run, consider moving the queries themselves to stored procedures,

instead of using ad hoc queries (see Article #2201). Use indexes on any column(s) used in the WHERE or

ORDER BY clauses (see Article #2231). Increasing timeouts is not a solution! It's more like painting your car

bright orange to draw attention from a door ding.

■ Use SET NOCOUNT ON in all procedures:

CREATE PROCEDURE foo


AS
BEGIN
SET NOCOUNT ON
-- ...
END

This prevents "1 row(s) affected." messages from being sent back over the wire, prevents SQL Server from

working to obtain those numbers, and prevents ADO from tripping over recordsets that aren't really

recordsets. If you need to know the number of rows affected while debugging, set up PRINT @@ROWCOUNT

calls on the following line, or store them all in a table and SELECT from them *after* all your other SELECT

statements, that way you can view such results in Query Analyzer without interfering with your ASP code.

■ Make sure your network connection between web server and database server is not the bottleneck. You can

test this by sitting at (or terminal serving into) the database machine directly, and executing the query from

its own Query Analyzer, and comparing the times with your ASP results (see Article #2092 and Article

#2245 for some timing ideas).

■ Limit resultsets when possible. For example, if you are returning a student directory, make the user restrict

the results to those whose last name starts with G or M. This will reduce the work you are forcing your

database and web server(s) to perform, and also making the interface more usable (it's very rare that the

end user needs to see the entire table in one shot). You could also consider paging (see Article #2120) -

breaking the recordset into chunks across multiple pages.

■ Do not nest recordsets in ASP! The database is faster at grouping/processing rows. If you think you need
nested recordsets, consider a JOIN. If you are having problems with an appropriate JOIN strategy, please

post your table structure(s), sample data and desired results, and others will help you get a correct query

working.

■ Do not use rs.movefirst when using a default, forward-only recordset. Set up SQL Profiler when doing this,

to see why.

■ Avoid extra ADODB objects unless necessary (see Article #2191). If you are using ADODB.Recordset or

ADODB.Command to execute stored procedures or INSERT/UPDATE/DELETE rows in a table, consider using

the connection object by itself.

■ Use the adExecuteNoRecords constant for INSERT, UPDATE and DELETE queries:

conn.execute sql, , &H00000080

■ When designing your tables, use the narrowest columns possible. If you have a numeric value that will be

from 1-10, use a tinyint, not a smallint or an int. If it can only be 0 or 1, use a BIT. If you have a column for

a Social security number, use INT or CHAR(9) (or CHAR(11), if it has to be string formatted for some

reason), as opposed to a BIGINT or VARCHAR(50). Choose character-based datatypes appropriately (see

Article #2354). If second- and millisecond-accuracy are not important, use a smalldatetime instead of a

datetime.

■ Avoid cursors if possible - most cursor solutions have a more efficient, set-based alternative. If you can't find

a set-based solution, post your table structure, sample data, and desired results to

microsoft.public.sqlserver.programming and someone will help you. If you still need to use a cursor, or if the

cursor solution is faster (rare, but possible), make sure you close and deallocate the cursor when you are

done with it.

■ Only get the data you need - avoid unnecessary columns, frivolous JOINs, and the all-too-common SELECT *

(see Article #2096).

■ Avoid DISTINCT unless it is absolutely necessary.

■ Avoid NULLs unless they are necessary (see Article #2073).


■ Use derived tables in place of temp tables / table variables.

■ Use local temp tables in place of global temp tables. Make sure you drop all #temp tables at the end of your

procedures (otherwise you will find tempdb continuously growing).

■ Choose constraints over triggers when given the choice. Assuming you have decent error-handling in place,

this is a more efficient way to regulate your data and prevent invalid data from entering into your database.

■ Test the performance differences of IN vs. EXISTS, ISNULL vs. COALESCE, each of which can provide the

exact same results, but often the optimizer can choose a better plan in one case or the other. The

performance can differ based on the datatype(s), size of table, number of relevant rows, and other factors.

■ Try to avoid non-sargable conditions in the WHERE clause, such as "IS NULL", "IS NOT NULL", "OR", "<>",

"!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", "LIKE"... any non-sargable arguments will not

use an index and will almost always result in a table scan (trust me, you don't like table scans).

■ Make sure you are either committing or rolling back all T-SQL transactions. If a query is not fully committed

or rolled back, you can exceed the concurrent query limit, and can cause blocking for all other SPIDs. Check

@@TRANCOUNT within the SPID's connection, if possible, and issue a ROLLBACK if it is not equal to 0.

■ For tips on optimizing Access performance, see MSDN's Ways to optimize query performance.
What is Event ID 36, and how can I get IIS running again?

1,301 requests - last updated Sunday, September 15, 2002

Many people have discovered Event ID 36 in their Event Log, and associate it with a time where ASP stopped serving

on their IIS 5.0 server (HTML continues to serve fine). This is usually associated with a failure with an error number

of 80004002, 8002801c or 80040154. This particular error is often associated with the message "No Such Interface

Supported"... which is a bit ambiguous, to say the least. It can also be "The server failed to load application

'/LM/W3SVC/1/Root'" or "The specified metadata was not found." Here are the various solutions that have worked

for different people with this error, from conservative to aggressive:

- Issue an IISRESET() call, which will restart the IIS-related services.

- Check that IIS' Application Mappings for ASP are intact, under IIS' Home Directory / Configuration interface.

- Move the Application from high to medium to low application protection, re-testing your ASP pages each time.

- Remove the Application and recreate it.

- Re-synchronize your IWAM account by running C:\inetpub\Adminscripts\synciwam.vbs (see Q255770 for more

info).

- stop IIS, run the following commands, then restart IIS:

regsvr32 c:\winnt\system32\oleaut32.dll
regsvr32 c:\winnt\system32\inetsrv\asp.dll

- Reinstall the distributed transaction coordinator from C:\WINNT\system32\dtcsetup.exe

- Apply Windows 2000 SP2, MDAC 2.6 or 2.7, and all relevant security fixes.

- If you have Crystal Reports 8 installed, uninstall IIS 5.0 from Add/Remove Programs, install the Seagate fix,

reinstall IIS 5.0, and reboot.

- See Q297519, Q271071, Q257267, Q238665, Q195956


Should I use the .inc extension for my include files?

1,295 requests - last updated Wednesday, March 13, 2002

Not if you value any of the ASP code they contain. There are sites all over the web that use .inc file to hold their

database connection info and other data that should remain on the server side; they think that information is safe

because they haven't told anybody where it is. Well, I remember several times I would get an error on someone's

site, and the error message would tell me:

Error Type:
Microsoft VBScript runtime (0x800A01B6)
Object doesn't support this property or method: 'rs.moveOver'
/includes/database.inc, line 3

So, I would type in the URL to /includes/database.inc file and, lo and behold, the browser would dump out the ASP

code (in a few cases, with sa passwords!). In fact just now, it took me all of five minutes to find a username /

password through a search on Google, register the server in SQL Enterprise Manager, and be tempted to execute a

command like:

TRUNCATE TABLE master..sysobjects

Of course I didn't do this, and promptly informed the server's owner about the hole (and that they should change

their password, pronto).

There are two decent workarounds that will prevent this from happening to you. One way is to leave your .inc files

alone, and simply map .inc extension to asp.dll (as described in Article #2124). The other is to simply use the .asp

extension on all files that include ASP code. If you need to differentiate between "includes" and normal interface

pages, simply change your naming scheme a bit. In one project I was involved in, the authors had realized the

security concerns and quickly renamed all files such as "include.inc" to "include.inc.asp" - so the files were still easily

identifiable as includes, but they were protected from casual viewing.

One concern people have raised in the past about the extension workaround is that now asp.dll will have to process

all "includes" as ASP files... even the ones that don't contain ASP code. Never fear, IIS 5.0 and above have much

smarter interpreters that only invoke the ASP engine when necessary.
Can I perform simple encryption / decryption in ASP?

1,282 requests - last updated Wednesday, February 6, 2002

Sure, however before you get into the code please understand that this is *not* secure in any way. In fact, it is

more like encoding than encrypting; it will protect the string from casual viewing, but will not be safe against anyone

with a purpose (and 5 minutes to kill). If you need encryption, see the list below the code for several free and

commercial offerings.

Basically, this code takes the ASCII value of each string, offsets it by a number you can control (don't go too high

though; I'd stick in the range of -47 -> 133 to be safe, as CHR() will return errors if you reach > 255). This

assumes, of course, that the original string contains ONLY alphanumeric characters. You will have to play with that

range if you are using other characters, and you will also want to test this code if you are using a non-default

character set.

<%
str = "ZzAa019WHOLEbunch"
offset = 8

for i = 1 to len(str)
newStr = newStr & chr((asc(mid(str,i,1))+offset))
next

response.write "New string is : " & newStr & "<p>"

for i = 1 to len(newStr)
oldStr = oldStr & chr((asc(mid(newStr,i,1))-offset))
next

response.write "Old string was : " & oldStr & "<p>"


%>

Components Available

a1asp.crypto
ABCCrypto

ASPEasyCrypt

ASPEncrypt

ASP XCrypt 2.0

ASP MagicCrypt

cASPer Encryption Package

Cast 128

CEncrypt

CryptToy

DigiSign 2.0

DynuEncrypt

EncryptCC

Hash

Polar Crypto

SloppyCode.Net
How do I change document names / extensions in IIS / PWS?

1,275 requests - last updated Monday, October 29, 2001

Open up Internet Services Manager. In IIS / PWS 4.0, this is on the Start Menu under Windows NT 4.0 Option Pack / Internet

Services Manager. In IIS 5.0 / Windows 2000, this is under Administrative Tools in the control panel. (I have a shortcut to this

tool on my taskbar; if you do a lot of work in the MMC tool for IIS, you may want to do the same.)

To change/add document names, for example if you want http://www.yoursite.com/ to display that way, but actually load

http://www.yoursite.com/my_goofy_file.asp, then follow these steps:

1. Right-click 'Default Web Site' (or the application in question)

2. Choose 'Properties'

3. On the 'Documents' tab, make sure 'Enable Default Document' is checked

4. Select the 'Add...' button, type in the filename you wish to override default.asp

5. Click OK, then click the up arrow next to the file list to push your new file to the top of the list

6. Click Apply, OK, and close IISAdmin (if you are asked to save console settings, choose Yes).
To add a documentation extension, for example if you want <file>.inc to actually run as an ASP file and be parsed by

ASP.DLL, then follow these steps:

1. Right-click 'Default Web Site' (or the application in question)

2. Choose 'Properties'

3. On the 'Home Directory' tab, choose 'Configuration...'

4. On the 'Mappings' tab, choose 'Add'

5. Under 'Executable' browse for ASP.DLL (located in %system%\inetsrv\)

6. For 'Extension' type in the extension you wish to use (including the leading period)

7. The only verbs you should need are GET and POST (see MSDN for information on HEAD, OPTIONS, TRACE etc.)

8. Click OK, Apply, OK, and close IISAdmin (if you are asked to save console settings, choose Yes).
How do I detect the browser's encryption level / cipher strength?

1,250 requests - last updated Sunday, June 10, 2001

In any HTTPS page, you can ask for:

<%
Response.Write Request.ServerVariables("HTTPS_KEYSIZE")
%>

Note that this information is not available to ASP until a SECURE connection has been negotiated.
Why do I get 'HTTP/1.0 Invalid Application Name' errors?

1,234 requests - last updated Thursday, September 14, 2000

There are a few KB articles which describe this error.

Q163501 FIX: INVALID APPLICATION NAME Error in Active Server Pages

Q172217 ASP Returns Invalid Application Name Error Message

Q189653 Exchange Virtual Directory Cannot Be IIS Home Directory


What do I do when IIS 5.0 will not start?

1,170 requests - last updated Wednesday, April 25, 2001

After installing / removing applications, or manually removing Client for Microsoft Networks directly, you may find

that IIS 5.0 suddenly will not start. Errors that may appear in the event log are:

■ The service did not respond to the start or control request in a timely fashion; or,

■ The authentification service is unknown.

There is a comprehensive article at IISFAQ.com that will help with this problem.
I have plenty of RAM, why do I get an 'Out of memory' error?

1,075 requests - last updated Wednesday, September 11, 2002

You may have seen this error before:

Microsoft VBScript runtime error '800a0007'


Out of memory

or

Microsoft VBScript compilation error '800a03e9'


Out of memory

This message has little to do with the amount of RAM you have. I have a workstation with 1 GB of RAM, and I have

seen this error when developing locally.

VBScript has inherent limitations on the amount of space it can allocate to, for example, arrays. So, if you try this:

<%
dim bigArray(7000,7000)

' or

el1 = 50
el2 = 400
el3 = 475
dim hugeArray()
redim hugeArray(el1, el2, el3)
%>

You will almost certainly get an error message.

Some other possible reasons exist... see Q174634, Q174685, and Q191099.
And I loved this response.
Why do I get non-database-related 80004005 errors?

1,035 requests - last updated Saturday, September 21, 2002

Here is a verbose collection of information surrounding 80004005 errors that have nothing to do with a database. If

your 80004005 error involves a database, please see Article #2009.

Luckily, most 80004005 errors that are not database-related are trivial to fix, though a few are a but more tricky

than that...

If there is no message associated with the error, there is at least one possible culprit: you tried to assign a negative

value to Server.ScriptTimeout. Make sure you pass Server.ScriptTimeout a value between 1 and 2^32-1 (see Article

#2066).

Active Server Pages, ASP 0100 (0x8000405)


Unable to allocate required memory

This is not usually about physical memory -- Article #2196 has some information about this error. In addition, if you

are getting this error when trying to connect to Project 2000 Server, make sure you are connecting to the root (e.g.

http://srvName/ProjectServer/) as opposed to trying to connect directly to a file in one of the sub-folders.

Request object error 'ASP 0101 : 80004005'


Unexpected error

This error message is covered quite extensively in Article #2383.


0x80004005
Unspecified error

If this is coming from use of MSXML.ServerXMLHTTP, see Article #2391.

Request object error 'ASP 0102 : 80004005'


Expecting string input

or

Request object, ASP 0102 (0x80004005)


The function expects a string as input.

This can be caused by passing an undeclared variable into Request.Form or Request.Querystring, e.g.

<%
Dim str
Response.Write(Request.QueryString(str))
' or
Response.Write(Request.Form(str))
%>

To correct, make sure your str variable is defined and populated with a valid incoming form variable (and make sure

you use the correct collection).

Active Server Pages, ASP 0103 (0x80004005)


Expecting numeric input

This can happen if you have a function in a VB or C++ DLL that is expecting an INT or LONG value, and you

accidentally pass a string. Check your argument(s) and make sure they match the datatypes that the component is
expecting. If you debug using response.write, you may see a value that *looks* numeric but you should always be

sure by using CInt(), CLng() or CDbl().

Request object, ASP 0105 (0x80004005)


An array index is out of range.

This is often caused by using a numeric argument in Request.QueryString or Request.Form, without checking that

such an index exists. For example:

<%
' ...
sql = "SELECT id FROM table"
set rs = conn.execute(sql)
do while not rs.eof
response.write Request.Form(rs("id"))
rs.movenext
loop
%>

At the line where this error is happening, response.write the index you are passing to the array or structure. It

should be clear that this number is either null or outside the dimensions of the array. For more information on

iterating through a form collection by numeric index, see Article #2036.


Request object, ASP 0107 (0x80004005)
The data being processed is over the allowed limit.

or

Request object error 'ASP 0107 : 80004005'


Stack Overflow
/<file>.asp, line <line>
The data being processed is over the allowed limit.

Each form field is limited to 102kb (according to Q273482)... for a verbose workaround, see

http://www.pstruh.cz/tips/detpg_largepost.htm. Or, use client-side validation to prevent submission of more than

102,399 characters in a single textarea.

Active Server Pages error 'ASP 0113 : 0x80004005'


Script timed out
<file>.asp, line <line>
The maximum amount of time for a script to execute was exceeded. You
can change this limit by specifying a new value for the property
Server.ScriptTimeOut or by changing the value in the IIS
administration tools.

See Article #2366 for a discussion of this error message.


Active Server Pages error 'ASP 0116 : 0x80004005'
Missing close of script delimiter
/<file>.asp, line <line>
The Script block lacks the close of script tag (%>).

or

Active Server Pages, ASP 0116 (0x80004005)


The Script block lacks the close of script tag (%>)

You have somehow embedded an opening script tag (<%) without closing it. A simple method to reproduce is as

follows:

<%table width=100>

If your code is really complex and you can't find the offending tag, start at line 2 and move a response.end

statement down one line at a time until you find it.

Active Server Pages, ASP 0117 (0x80004005)


The Script block lacks the close of script tag (</SCRIPT>) or close of tag symbol (>).

Active Server Pages, ASP 0117 (0x80004005)


The Script block lacks the close of script tag (%>).

You probably did this:

<script language=javascript runat=server>

or

<%
Without ever closing the tag. You may even have done this:

<script language=javascript runat=server>


</script

Notice the missing close brace (>)... it might have been accidentally carriage-returned to the next line.

Active Server Pages, ASP 0118 (0x80004005)


The Object block lacks the close of object tag (</OBJECT>) or close of tag symbol (>).

Similar to the above case, you either forgot to close your object tag at all, or left off the trailing close brace (>),

e.g.:

<object runat=server ... >


</object

Active Server Pages, ASP 0119 (0x80004005)


The object instance '<id>' requires a valid Classid or Progid in the object tag.

This error is self-explanatory. You have some variation of the following:

<object runat=server id=foo>


</object>

Of course, you need to tell ASP the ProgID or ClassID of the object you want to use, otherwise it will have no idea

which object to create.


Active Server Pages, ASP 0120 (0x80004005)
The Runat attribute of the Script tag or Object tag can only have the value 'Server'.

You probably have one of these:

<SCRIPT LANGUAGE="VBScript" RUNAT=CLIENT>

or

<OBJECT RUNAT=CLIENT>

If you are mixing server- and client-side scripts and/or objects, keep in mind that you only have to specify the

RUNAT property for those you wish to be interpreted server-side. Since client-side is the default, just leave the

RUNAT attribute out altogether.

Active Server Pages, ASP 0123 (0x80004005)


The required Id attribute of the Object tag is missing.

Again, this error is self-explanatory. Such elements need to have an ID associated with them, so that you can

actually refer to it later to call methods and properties (I prefer set foo = Server.CreateObject("Prog.ID"),

personally). So your existing code probably looks like this:

<object runat=server ProgID="Prog.ID">


</object>

Just give it an ID, as follows:

<object runat=server ProgID="Prog.ID" id="MyObject">


</object>
Active Server Pages, ASP 0124 (0x80004005)
The required Language attribute of the Script tag is missing.

You have a block of code that looks like this:

<script runat=server>
' ...
</script>

You need to specify the language. If you mean to use the default language, use <%%> delimiters instead. See

Article #2045 for information on how switching delimiter style can affect the way your script executes.

Active Server Pages, ASP 0125 (0x80004005)


The value of the '<attribute>' attribute has no closing delimiter.

This is another helpful error message. Check the line the error points to, and see if there are any attribute values

that have a missing trailing double-quote, e.g.:

<object id=foo runat=server progID="Prog.ID>

To fix, either remove the leading double-quote, or add a trailing double-quote.

Active Server Pages, ASP 0126 (0x80004005)


The include file '/<file>' was not found.
This is usually a simple error to fix... either you spelled the filename incorrectly, pointed to the wrong folder, or used

#include virtual instead of #include file (or vice-versa).

Active Server Pages, ASP 0127 (0x80004005)


The HTML comment or server-side include lacks the close tag (-->).

Look for code like this in your ASP script:

<!--#include file=foo.asp
<% ' stuff %>

or

<!-- this is a comment


<% ' stuff %>

Make sure your comments and include directives are closed correctly, using -->.

Active Server Pages, ASP 0128 (0x80004005)


Missing File or Virtual attribute
The Include file name must be specified using either the File or Virtual attribute.

This can happen if you use code like this:

<!--#include=whatever.asp-->

It should either be:


<!--#include file=whatever.asp-->
or
<!--#include virtual=/whatever.asp-->

Active Server Pages, ASP 0129 (0x80004005)


The scripting language '<script language>' is not found on the server.

You are attempting to use a language that is not installed, e.g. PerlScript. Make sure that you have installed all

dependencies for running a specific host language in your ASP scripts.

Active Server Pages, ASP 0130 (0x80004005)


Invalid File attribute
/<file>.asp, line <line>
File attribute '/<file>.asp' cannot start with forward
slash or back slash.

or

Active Server Pages, ASP 0131 (0x80004005)


Disallowed Parent Path
/<file>.asp, line <line>
The Include file '../<file>.asp' cannot contain '..' to
indicate the parent directory.

See Article #2412 for a decription and resolution of these error messages.
Active Server Pages, ASP 0133 (0x80004005)
Invalid ClassID attribute
/<file>.asp, line <line>
The object has an invalid ClassID of '<classID>'

or

Active Server Pages, ASP 0134 (0x80004005)


Invalid ProgID attribute
/<file>.asp, line <line>
The object has an invalid ProgID of '<prog.id>'

Usually, this is due to using a ProgID or ClassID that isn't properly registered on the system. For more information

about this error, see Article #2134.

Active Server Pages, ASP 0135 (0x80004005)


The file '<file.asp>' is included by itself (perhaps indirectly). Please
check include files for other Include statements.

Like the error suggests, an #include file is being included by itself somehow. While it may not contain an #include

reference to itself, it may include a file that does. This may take a while to find; if so, it may be a hint that you need

to simplify your include structure.

Active Server Pages, ASP 0136 (0x80004005)


The object instance '<id>' is attempting to use a reserved name.
This name is used by Active Server Pages intrinsic objects.

You are using an <OBJECT> tag and gave it an ID=Response or ID=Application. Make sure you avoid using intrinsic

names (others are Server, Session, and Request). You should also avoid using VBScript keywords like for, e.g. the

following code will error in two places, depending on which is first:


<object id=FOR runat=server progID="Scripting.FileSystemObject">
</object>
<%
for.deleteFile("c:\foo.txt")
set foo = for.openTextFile("c:\foo.txt")
%>

The first line will result in this error:

Microsoft VBScript compilation (0x800A03F2)


Expected identifier

The second line will result in this error:

Microsoft VBScript compilation (0x800A03EA)


Syntax error

Active Server Pages, ASP 0137 (0x80004005)


Script blocks must be one of the allowed Global.asa procedures. Script
directives within <% ... %> are not allowed within the Global.asa file. The
allowed procedure names are Application_OnStart, Application_OnEnd,
Session_OnStart, or Session_OnEnd.

You cannot place <% ' code %> within global.asa. Any code that executes within global.asa must do so within one

of the four functions mentioned in the error message.

Active Server Pages, ASP 0138 (0x80004005)


A script block cannot be placed inside another script block.
This is a problem with the preprocessor, and can happen with the following code -- which uses ASP to generate

client-side script:

<script language=vbscript runat=server>


response.write "<script></script>"
</script>

To work around this, you can either (a) use <%%> delimiters for the server-side script, or (b) break up the client-

side tag as follows:

<script language=vbscript runat=server>


response.write "<scr" & "ipt></scr" & "ipt>"
</script>

Active Server Pages, ASP 0139 (0x80004005)


An object tag cannot be placed inside another object tag.

Again, a self-explanatory error message. Make sure you haven't done this:

<object runat=server ...>


<object runat=server ...>
</object>
</object>

Active Server Pages, ASP 0140 (0x80004005)


The @ command must be the first command within the Active Server Page.

Like the error states, you can't place an @ directive tag after any other content in an ASP page.
Active Server Pages, ASP 0141 (0x80004005)
The @ command can only be used once within the Active Server Page.

This error means just what it sounds like; you should only have one @ directive in any ASP page. You should check

any includes, if you only see one @ symbol in the base page. This is probably due to including two files that both use

@language or other @ directives, but it is possible that you have two directives in the same page. It is also possible

that you "need" two @ directives, for example if you need to specify a language and a codepage. You can work

around this by using <script runat=server> and/or session.LCID syntax. See Q307190 for more info.

Response object error 'ASP 0156 : 80004005'


Header Error
/<file>.asp, line <line>
The HTTP headers are already written to the client browser. Any HTTP header
modifications must be made before writing page content.

You can't use response.redirect or response.cookies after sending any HTML content to the browser, unless you have

enabled response.buffer = true. For more information, see Article #2011 and Article #2217. This error can also

happen if you write HTML content to the browser before setting a response. property, e.g.:

<html><% response.buffer = true %>

Response object error 'ASP 0157 : 80004005'


Buffering On
/<file>.asp, line <line>
Buffering cannot be turned off once it is already turned on.

If you have enabled buffering at the site/application level, you can not disable it at page scope. See Article #2262
for more information.

Response object error 'ASP 0158 : 80004005'


Missing URL
/<file>.asp, line <line>
A URL is required.

See Article #2375 for more information on this specific error message.

Response object error 'ASP 0159 : 80004005'


Buffering Off
/<file>.asp, line <line>
Buffering must be on.

This can happen if you have disabled buffering and try to use buffer-dependent methods such as response.clear() or

response.flush(). See Article #2262 for information about enabling buffering at the site / application level.

Session object, ASP 0168 (0x80004005)


An intrinsic object cannot be stored within the Session object.

This often happens when using JavaScript to retrieve Request.Form or Request.QueryString values, and assigning

them to session variables. For example:

<script language=JavaScript runat=server>


Session("variable") = Request.QueryString("foo");
</script>
Keeping in mind that Request.QueryString objects are objects, make sure to get the value of the object instead:

<script language=JavaScript runat=server>


Session("variable") = Request.QueryString("foo").value;
</script>

Server.MapPath() error 'ASP 0171 : 80004005'


Missing Path
/<file>.asp, line <line>
The Path parameter must be specified for the MapPath method.

This error message is pretty self-explanatory. You have used server.mappath() but you have either not passed it a

value or the variable you passed did not contain the value you expected. Debug the line that is causing the error by

changing this:

lPath = Server.MapPath(vPath)

To this:

Response.Write(vPath)
Response.End
'lPath = Server.MapPath(vPath)

Server.MapPath() error 'ASP 0172 : 80004005'


Invalid Path
/<file>.asp, line <line>
The Path parameter for the MapPath method must be a virtual path. A physical
path was used.
Like the error says, you can only use relative paths with Server.MapPath, since the purpose of the function is to

generate a local/physical path from a web structure. So, if you have the physical path already, don't bother calling

Server.MapPath().

Server.MapPath(), ASP 0173 (0x80004005)


An invalid character was specified in the Path parameter for the MapPath method.

or

Server.MapPath() error 'ASP 0174: 80004005'


Invalid Path Character(s)
<file>.asp, line <line>
An invalid '/' or '\' was found in the Path parameter for the MapPath method.

This error message is discussed at length in Article #2302.

Server.MapPath(), ASP 0175 (0x80004005)


The '..' characters are not allowed in the Path parameter for the MapPath
method.

If you are trying to give a reference to a file in a parent folder, you might have to work around this problem by using

a path relative from the root, rather than relative to the current folder.

Server.MapPath(), ASP 0176 (0x80004005)


The path parameter for the MapPath method did not correspond to a known
path.

Check that the path you are passing into the MapPath method makes sense. Try to use a path relative to the root.
Active Server Pages, ASP 0194 (0x80004005)
An error occurred in the OnEndPage method of an external object.

This error message is discussed in Article #2171.

Application object, ASP 0197 (0x80004005)


Cannot add object with apartment model behavior to the application intrinsic
object.

See Article #2053 for several links on the limitations of components written using the apartment model.

Request object, ASP 0206 (0x80004005)


Cannot call BinaryRead after using Request.Form collection

Request object, ASP 0207 (0x80004005)


Cannot use Request.Form collection after calling BinaryRead.

Request object, ASP 0208 (0x80004005)


Cannot use the generic Request collection after calling BinaryRead.

When you are uploading files, you need to use a different mechanism to get the text from any 'regular' form

controls. See Article #2166 for more information.

Response object, ASP 0211 (0x80004005)


A built-in ASP object has been referenced, which is no longer valid.
This can happen if you reference an application- or session-level object in application_onEnd or session_onEnd, and

the object no longer exists. You should peruse the links in Article #2053 to see why you should rarely be using any

type of object in application or session scope.

Active Server Pages, ASP 0217 (0x80004005)


Object scope must be Page, Session or Application.

You might have something along these lines:

<object id=foo runat=server progID="Prog.ID" scope=foo>

As the error states, the only valid scope values are Page, Session and Application. However, it is very rare that you

would be creating an object for application or session scope (see Article #2053), and since the default is page, you

won't normally need to include the scope attribute in your object tags at all.

Response object error 'ASP 0219 : 80004005'


Invalid LCID
/<file>.asp, line <line>
The specified LCID is not available.

or

Session object, ASP 0204 (0x80004005)


Invalid CodePage Value

This can happen if you try to specify an LCID / CodePage that your system isn't set up to handle, e.g. on a default

install:
<%
ShowLocaleDate 1041, "Japan"

Sub ShowLocaleDate(lcid, Locale)


Response.LCID = lcid
Response.Write "<B>" & Locale & "</B><BR>"
Response.Write FormatDateTime(Date, 1)
End Sub
%>

To fix, either use a different LCID, or you will need to install support for far east languages (in the case of Japan) or

whatever codepage you need to use (e.g. support for right-to-left languages, if you need to support Arabic or

Indian), through the "regional settings" applet in the control panel. Here is where these options live in .NET Server:

Active Server Pages, ASP 0221 (0x80004005)


The specified '<text>' option is unknown or invalid.

If <text> is:
SCRIPT LANGUAGE="VBSCRIPT"

It is likely that you attempted this:

<%@ SCRIPT LANGUAGE="VBScript" %>

The SCRIPT keyword is redundant here. Your tag should look like this:

<%@ LANGUAGE="VBScript" %>

runat=server

It is likely that you attempted this:

<%@ LANGUAGE="VBScript" RUNAT=SERVER %>

The runat option is unnecessary in an <%@ %> directive, because the tag is already interpreted only in server-side

script. The runat=server option only belongs in a server-side script defined with the following syntax:

<Script Language=VBScript Runat=Server>

Page Language="vb"

You are attempting to use VB.Net in an ASP page. You should be using an ASP.NET page, with an ASPX extension.
Active Server Pages, ASP 0223 (0x80004005)
METADATA tag contains a Type Library specification that does not match any
Registry entry.

or

Active Server Pages, ASP 0224 (0x80004005)


Cannot load Type Library specified in the METADATA tag.

Double-check your METADATA tags. One of them points to an invalid file or ProgID. For valid METADATA tags to use

with MDAC 2.5, 2.6 and 2.7, see Article #2112.

Server object, ASP 0228 (0x80004005)


The call to Server.Execute failed while loading the page.

or

Server object, ASP 0230 (0x80004005)


The call to Server.Transfer failed while loading the page.

Check the URL that you are passing to the transfer or execute method. Make sure it is a relative URL, and that you

can hit it with a browser. Response.Write a Server.MapPath on the value you are passing, and make sure it produces

a valid local path.


Server object, ASP 0231 (0x80004005)
Invalid URL form or fully-qualified absolute URL was used. Use relative
URLs.

or

Server object, ASP 0235 (0x80004005)


Invalid URL form or fully-qualified absolute URL was used. Use relative
URLs.

With Server.Transfer and Server.Execute, you cannot pass absolute (e.g. http://url/ or c:\file\) URLs, nor can you

pass querystrings. The target page will have access to If you are meaning to execute a page on a different server,

see Article #2173 for a small tutorial on using the XMLHTTP object.

Active Server Pages, ASP 0234 (0x80004005)


Server side include directives may not be present in
script blocks. Please use the SRC= attribute of the
<SCRIPT> tag.

This can happen when you try and implement the following code:

<script language=vbscript runat=server>


<!--#include file='rw.asp'-->
</script>

The following syntax doesn't choke, but the #include file does not execute.

<%
<!--#include file='rw.asp'-->
%>

There is no reason to put the #include directive inside an existing ASP code block.
Active Server Pages, ASP 0238 (0x80004005)
No value was specified for the 'version' attribute.

You are probably including a METADATA tag for XML in the following format:

<!--METADATA TYPE="TypeLib" NAME="Microsoft XML, version 2.0"


UUID="{D63E0CE2-A0A2-11D0-9C02-00C04FC99C8E}" VERSION="2.0"-->

Note the following change, which should alleviate the error:

<!--METADATA TYPE="TypeLib" NAME="Microsoft XML, v2.0"


UUID="{D63E0CE2-A0A2-11D0-9C02-00C04FC99C8E}" VERSION="2.0"-->

However a more appropriate fix would be to install / use a more recent version of the XML SDK.

Active Server Pages, ASP 0238 (0x80004005)


No value was specified for the 'PROGID' attribute.

Check your METADATA tag, it is either missing the PROGID attribute entirely, or it is empty.

Active Server Pages, ASP 0239 (0x80004005)


UNICODE ASP files are not supported.

Make sure you have installed language support for the language you want to use, and that you have correctly set

the Session.LCID or Session.Codepage to the appropriate value. See Q245000 for more information.
Active Server Pages, ASP 0240 (0x80004005)
A ScriptEngine threw expection 'C0000005' in
'IActiveScript::GetScriptState()' from 'CActiveScriptEngine::ReuseEngine()'.

or

Active Server Pages, ASP 0241 (0x80004005)


The CreateObject of '(null)' caused exception E06D7363.

For a lengthy discussion of these error messages, see Article #2355.

Active Server Pages, ASP 0243 (0x80004005)


Only METADATA TYPE="TypeLib" may be used in Global.asa.

Sounds like another case of a malformed METADATA tag. Check your syntax and double-check it against the source.

If you know of any other 80004005 errors that we haven't covered here, please let us know...
How do I solve 'The specified procedure could not be found' errors?

1,026 requests - last updated Thursday, May 10, 2001

This issue will come up if you fixed the "f**k Government" worm issue, outlined and patched at the following URL:

MS00-086

You can resolve this issue simply by (re-)applying NT 4.0 Service Pack 6a.

Visit Microsoft's security site for other security bulletins, patches and updates.
How do I display the Euro symbol ( ) in my ASP pages?

1,012 requests - last updated Friday, March 22, 2002

Being a relatively new concept, the Euro is not covered under any special Session.LCID value (at least not that I

know of) for use with VBScript's FormatCurrency function. But there is an HTML entity to represent the character, so

it is trivial to wrap a presentation layer for such currency in your own function. I tend to use emphasis on the

character as it is a rather weak-looking HTML entity.

<%
Function FormatEuro(n)
If IsNumeric(n) Then
FormatEuro = "<b>&#8364;</b>" & FormatNumber(n,2)
Else
FormatEuro = "<b>&#8364;</b>" & n
End If
End Function

Response.Write(FormatEuro(48.24))
%>
How do I log / track ASP errors on my web site?

991 requests - last updated Tuesday, August 6, 2002

In IIS 5.0 and above, there is a new object called the ASPError object, which allows you to have more control over how your

errors appear to users, and how you can track these errors and prevent them from occuring in the future. This article will

quickly guide you through creating a tiny application that will track 500 errors and allow you to review them at the end of the

day. The article assumes you are using SQL Server as your database backend, but could be modified easily to work with

Access or any other database platform.

Before we start, let's force a pretty simple 500 error (save it as <wwwroot>\force500error.asp) with the following code:

<%
functionThatDoesNotExist()
%>

This should result in the following error:

Microsoft VBScript runtime error '800a000d'


Type mismatch: 'functionThatDoesNotExist'
/force500error.asp, line 2

Now, let's create a simple example of a 500 error handler page. First, create the following ASP page:

<%
Set ASPErr = Server.GetLastError
Response.Write ASPErr.Description
%>

Save this file in <wwwroot>\500_try.asp (the default file in IIS for handling custom 500 errors is called 500-100.asp - we

should leave this file alone, in case we need to restore the default behavior at some point). Now, go into Internet Services

Manager, right-click the Default Web Site (or any web site or application where you want to isolate this test), choose

properties, on the Custom Errors tab, scroll down to 500;100 and hit Edit, verify that Message Type has "URL" selected, and

make sure the URL points to /500_try.asp (or wherever you placed it).
Once you've done this, you should get a prettier error when you hit force500error.asp:

Type mismatch: 'functionThatDoesNotExist'

That's just for starters. You can customize the 500 handler page later by adding your web site graphics, navigation, and even

other content. Keep in mind that, like the custom 404 error page described in Article #2162, path is important when defining

the images and other references in your 500 handler page. If this page is expected to handle errors in ASP pages in the root

of your site / application as well as sub-folders, all possible locations need to be aware of the exact path to any referenced

files.

All right, so let's create a table that will store the information we might want to track. In addition to the extra information

provided by the ASPError object that is not available with the traditional ON ERROR RESUME NEXT architecture, it might also

be useful to store referrer, user agent, ip and post data as well. So our table will look like this (and since we are going to
query by date after the fact, we put a clustered index on the datetime column):

CREATE TABLE HTTP500Errors


(
id INT IDENTITY(1,1),
dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
url VARCHAR(1024) NOT NULL DEFAULT '',
aspDesc VARCHAR(512) NOT NULL DEFAULT '',
aspNumber VARCHAR(16) NOT NULL DEFAULT '',
aspLine INT NOT NULL DEFAULT 0,
aspColumn INT NOT NULL DEFAULT 0,
errDesc VARCHAR(512) NOT NULL DEFAULT '',
errNumber VARCHAR(16) NOT NULL DEFAULT '',
ref VARCHAR(1024) NOT NULL DEFAULT '',
ip VARCHAR(15) NOT NULL DEFAULT '',
ua VARCHAR(1024) NOT NULL DEFAULT '',
method VARCHAR(4) NOT NULL DEFAULT 'GET',
data TEXT NOT NULL DEFAULT ''
)
GO

CREATE CLUSTERED INDEX ci_HTTP500Errors_dt ON HTTP500Errors(dt)


GO

-- replace ??? with the user you connect to from ASP


GRANT DELETE, INSERT, SELECT, UPDATE ON HTTP500Errors TO [???]
GO

And the following stored procedure will facilitate inserting data into this table when an error occurs:

CREATE PROCEDURE dbo.logHTTP500Errors


@url VARCHAR(1024)='',
@aspDesc VARCHAR(512)='',
@aspNumber VARCHAR(16)='',
@aspLine INT=0,
@aspColumn INT=0,
@errDesc VARCHAR(512)='',
@errNumber VARCHAR(16)='',
@ref VARCHAR(1024)='',
@ip VARCHAR(15)='',
@ua VARCHAR(1024)='',
@method VARCHAR(4)='GET',
@data TEXT=''
AS
BEGIN
INSERT HTTP500Errors
(
url, aspDesc, aspNumber, aspLine, aspColumn,
errDesc, errNumber, ref, ip, ua, method, data
)
VALUES
(
@url, @aspDesc, @aspNumber, @aspLine, @aspColumn,
@errDesc, @errNumber, @ref, @ip, @ua, @method, @data
)
END
GO

-- replace ??? with the user you connect to from ASP


GRANT EXEC ON logHTTP500Errors TO [???]
GO

So now that we have a table to store our data, and a stored procedure to interface with it, let's create the following ASP page

to replace 500_try.asp from above:

<%
Response.Status = "500 Internal Server Error"

Set ASPErr = Server.GetLastError()

' build the URL string


strURLPrefix = "http://"
if Request.ServerVariables("HTTPS") <> "off" then strURLPrefix = "https://"
strPageName = Request.ServerVariables("SCRIPT_NAME")
strServerName = Request.ServerVariables("SERVER_NAME")
strURL = strURLPrefix & strServerName & strPageName

' get the extended error information


strASPDesc = ASPErr.ASPDescription
strASPNumber = ASPErr.ASPCode
intASPLine = clng(ASPErr.Line)
intASPColumn = clng(ASPErr.Column)
strDesc = ASPErr.Description
strNumber = "0x" & Hex(ASPErr.Number)

' and some extra info, such as user agent


strRef = Request.ServerVariables("HTTP_REFERER")
strIP = Request.ServerVariables("REMOTE_ADDR")
strUA = Request.ServerVariables("HTTP_USER_AGENT")
strMethod = Request.ServerVariables("REQUEST_METHOD")
strData = Request.QueryString()
if strMethod = "POST" then strData = Request.Form()

' let's build our SQL string:


sql = "EXEC dbo.logHTTP500Errors " & _
" @url='" & fixSQL(strURL, 1024) & "'," & _
" @aspDesc='" & fixSQL(strASPDesc, 512) & "'," & _
" @aspNumber='" & fixSQL(strASPNumber, 16) & "'," & _
" @aspLine=" & intASPLine & "," & _
" @aspColumn=" & intASPColumn & "," & _
" @errDesc='" & fixSQL(strDesc, 512) & "'," & _
" @errNumber='" & fixSQL(strNumber, 16) & "'," & _
" @ref='" & fixSQL(strRef, 1024) & "'," & _
" @ip='" & fixSQL(strIP, 16) & "'," & _
" @ua='" & fixSQL(strUA, 1024) & "'," & _
" @method='" & fixSQL(strMethod, 4) & "'," & _
" @data='" & fixSQL(strData, len(strData)) & "'"

set conn = Server.CreateObject("ADODB.Connection")


' don't forget to update your connection string!
conn.open "<connection string>"
conn.execute(sql)
conn.close
set conn = nothing

' a little function to trim the data in case some of it


' exceeds bounds, and replace those pesky apostrophes
function fixSQL(str, intLength)
fixSQL = left(replace(str,"'","''"), intLength)
end function

' tell the user something (you can enter your


' site's graphics and navigation etc. here)
Response.Write "There was an error on this page. The server " & _
" administrator has been notified and will investigate."
%>

This ASP page will store all the relevant data. You can test it by hitting force500error.asp again, and then using Query

Analyzer to run a quick SELECT * query:

SELECT * FROM HTTP500Errors

You should see the results of any 500 errors that have occured since you set up the table and new 500 ASP page.

Now here is a stored procedure that will return all of the 500 errors that occurred during the current day (you can modify this

to go back to the beginning of the week, or the beginning of the month if you like):

CREATE PROCEDURE dbo.retrieveHTTP500ErrorsToday


AS
BEGIN
DECLARE @dt DATETIME
SET @dt = CONVERT(DATETIME, CONVERT(CHAR(10), GETDATE(), 101))
SELECT
id, dt, url, aspDesc, aspNumber, aspLine, aspColumn,
errDesc, errNumber, ref, ip, ua, method, data
FROM
HTTP500Errors
WHERE dt >= @dt
ORDER BY dt
END
GO

-- replace ??? with the user you connect to from ASP


GRANT EXEC ON dbo.retrieveHTTP500ErrorsToday TO [???]
GO

And here is a crude ASP page to review the results:


<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC dbo.retrieveHTTP500ErrorsToday")
if not rs.eof then
do while not rs.eof
response.write "<hr size=1>"
response.write rs("errNumber") & ": " & rs("errDesc") & "<br>"
response.write rs("url") & "<br>"
response.write rs("dt")
response.write " (link to details record here...)"
rs.movenext
loop
response.write "<hr size=1>"
else
response.write "No 500 errors today."
end if
conn.close
set conn = nothing
%>

I'll leave the extended details page as an exercise for the reader.

Note that Server.GetLastError and the ASPError object are *only* available in the custom 500 error handler. In your normal

ASP pages, they will not work as illustrated above. For more information, see the ASPError Object and Server.GetLastError

topics in MSDN's Online Library.


Why do I get an error about a 'Smart HTML interpreter'?

926 requests - last updated Wednesday, April 11, 2001

This seems to be a pretty common error with (shudder) FrontPage 2000 Server Extensions.

For possible resolution, see Q205540 and/or Q261971...


Why do I get 'Type Mismatch' when using the Session object?

893 requests - last updated Monday, July 8, 2002

This is usually because session state has been disabled, either through the Internet Services Manager GUI, or with

the @EnableSessionState directive. To use the Session object, session state must be enabled for the application.
How do I get the computer name / IP address of the server?

874 requests - last updated Wednesday, September 25, 2002

Here is a small code sample using WScript to get the computer name, in addition to members of the ServerVariables

collection for returning the domain name entered in the browser and the IP of the server.

<%
Set pc = Server.CreateObject("Wscript.Network")
response.write pc.ComputerName
Set pc = nothing
response.write " (currently connected as "
response.write ucase(Request.ServerVariables("SERVER_NAME"))
response.write " on "
response.write Request.ServerVariables("LOCAL_ADDR") & ")"
%>

Of course, you may want the actual computer name (from system properties), not the server name as requested

through a web server (which could be an IP address, or an alias, or a fully qualified domain name). So, you can use

WMI:

<%
Set Loc = Server.createobject("WBEMScripting.SWBEMLocator")
Set nms = Loc.ConnectServer()
WQL = "SELECT CSName FROM Win32_OperatingSystem"
Set cs = nms.ExecQuery(WQL, "WQL", 48)
For Each pc In cs
Response.Write pc.CSName
Next
set cs = nothing: set nms = nothing: set loc = nothing
%>

And here is a sample for getting the name of a SQL Server machine:
<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open "<connectionString>"
set rs = conn.execute("SELECT @@SERVERNAME")
response.write rs(0)
rs.close: set rs = nothing
conn.close: set conn = nothing
%>
Why do I get 'HTTP 500-12 Application Restarting' errors?

862 requests - last updated Friday, August 16, 2002

If you have extremely long processes that are processed during Application_OnStart or Session_OnStart, you might

occasionally see this message. If so, consider moving such processes to an offline source (e.g. for pure database

processing, use a job in SQL Server; for ASP tasks, see Article #2143 to learn how to schedule a VBS file instead).

This could be due to McAfee running on the server. According to Q303881, it marks global.asa as modified during

each scan, and this causes IIS to reload global.asa. If this is the case, you need to reconsider your virus protection.

Like Norton, using a desktop application on a server can have unexpected results.

See Q236446 if you are running Site Server 3.0, and try commenting out any instance of the 'ReloadSite' method.

If none of the above solves the problem, you could check your event log for any ASP-, IIS- or MTS-related errors.

This may reveal a deeper problem.

Other than that, even Microsoft doesn't have much to offer. They say "the message is harmless" and that "there is

no good workaround" ... check out Q248013 for more details.


How do I protect my images and other visual content?

811 requests - last updated Sunday, June 30, 2002

In short, you can't. If you don't want people using your images or layout ideas, don't put them on the web. There

are many ways to retrieve images, so don't bother using client-side JavaScript to trap right-click events (I can get

around that simply by disabling JavaScript, using a browser without onContextMenu support, or using my

'PrintScreen' button). However, to get an exhaustive tutorial for preventing casual surfers from using the common

methods of saving images, see http://pubs.logicalexpressions.com/Pub0009/LPMArticle.asp?ID=41 ...


Why are people telling me to fix my clock / timezone?

798 requests - last updated Thursday, February 21, 2002

Most people organize their message lists by date received, descending. This is so that the most recent posts are at

the top. Unfortunately, most NNTP servers do not reflect the date / time a post is received, but rather figure it out

based on the date / time the client SAID they posted it, adjust for THEIR timezone, then perform an offset for the

client's timezone.

Recognizing this, many people set their clocks ahead (by hours, days or even years) in an attempt to keep their

posts at the top of the list. Others simply have their timezone set incorrectly. In both cases, the post remains at the

top of the list for a short amount of time or, in extreme cases, until the server purges the messages (this is a few

weeks in the case of msnews.microsoft.com).

When confronted, inevitably someone rushes to their defense and says "well, maybe they're in the UK, and it's five

hours later there." Incorrect; the news server adjusts for that. Note that with normal clock settings, a conversation

between myself (East Coast USA) and someone in the UK would look like this to me:

+ my post 2:30 pm

- uk post 2:42 pm

And would look like this to the person in the UK:

+ my post 7:30 pm

- uk post 7:42 pm

And would look like this to an observer in Los Angeles:

+ my post 11:30 am

- uk post 11:42 am

This is the beauty of the newsgroup... posts follow a logical chronological order, no matter where you're viewing the

conversation. You send a post at 2:00 am your time, and I answer you at 2:45 am your time. In your newsreader, it

shows the entire conversation took place in 45 minutes. Was I up at 2:45 am? No. Did you wait only 45 minutes for

the response? Yes. Maybe it was 6:00 pm my time when I read your initial response. I answered at 6:45 pm my

time. My newsreader also shows the entire conversation took place in 45 minutes. This is not a coincidence people.
Some suggest ignoring such posts, but I am a firm believer in education. If you ignore the post, they post again

(likely still with an incorrect clock), thinking that nobody saw their original message. And often people don't notice

the post is future-dated, and respond. This prevents the original poster from knowing their clock is wrong (maybe it

was an honest mistake), and if they do know, it sends the message that it's okay to violate netiquette rules. So get

used to people complaining if you post with a future date.

Using Outlook Express, there are at least four ways to eliminate this clutter from the top of your message list:

■ hit Shift+F3, click Advanced Find..., fill in the criteria that will allow you to identify the post(s), click Find

Now, right click the message(s) and choose "Move to Folder" then browse to the deleted items folder;

■ block the sender entirely (this is a bit harsh though!) using Message | Block Sender;

■ set your options in Tools | Options | Maintenance to delete message more than x days old (it does a diff, not

an absolute, so will filter messages from the future also - but will only filter messages beyond today); or,

■ sort your message list using different criteria.


How do I solve 'Event ID 5' errors?

792 requests - last updated Friday, September 27, 2002

Event ID 5 seems to be the generic catch-all ASP error logged to the event log. There is probably a lot more out

there than what we've compiled here, but hopefully the following list of symptoms / resolutions will help you.

If you are seeing the following errors:

Script Engine Exception. A ScriptEngine threw expection 'C0000005'


in 'IActiveScript::SetScriptState()' from 'CActiveScriptEngine::ResetToUninitialized()'

or

Script Engine Exception. A ScriptEngine threw expection 'C0000005'


in 'IActiveScript::Close()' from 'CActiveScriptEngine::FinalRelease()'..

or

File /<file>.asp. Unexpected error. A trappable error (C0000005)


occurred in an external object. The script cannot continue running.

This is usually an access violation error, but since it's coming from a component that the error message fails to

identify, the only advice I can offer is to follow a similar debugging path as the ASP 0115 error (see Article #2171

and Q262187). Sometimes the error is accompanied by an Event ID 4014 error, which can often contain CLSID

information that will lead you to the component causing the error (you can search for the IID / CLSID string in the

registry).

Before you go the long debugging route, however, consider upgrading the server to the most recent version of

MDAC (http://www.microsoft.com/data/) and Windows Script version 5.6 (http://msdn.microsoft.com/scripting/)

and reboot... and make sure your server has the latest updates (see Article #2151). While this is not a proper, 'feel-

good' debugging effort, the above steps have often resolved the problem entirely.

If you are getting an E06D7363 error in the following form:


Script Engine Exception. A ScriptEngine threw expection 'E06D7363'
in 'IActiveScript::SetScriptState()' from 'CActiveScriptEngine::ResetToUninitialized()'.

or

Script Engine Exception. A ScriptEngine threw expection 'E06D7363'


in 'IActiveScript::Close()' from 'CActiveScriptEngine::FinalRelease()'.

Uninstall Norton CrashGuard, as it is infamous for 'inventing' errors in kernel32.dll with this error code.

If these errors are accompanied by the following ASP errors:

Active Server Pages, ASP 0241 (0x80004005)


The CreateObject of '(null)' caused exception E06D7363.

or

Active Server Pages, ASP 0240 (0x80004005)


A ScriptEngine threw expection 'C0000005' in
'IActiveScript::GetScriptState()' from 'CActiveScriptEngine::ReuseEngine()'.

or

Active Server Pages, ASP 0240 (0x80004005)


A ScriptEngine threw expection 'C00000FD'
in 'IActiveScriptParse::ParseScriptText()' from 'CActiveScriptEngine::AddScriptlet()'.

or

Server object, ASP 0177 (0x8007000E)


Ran out of memory

This is probably a permissions issue. Whatever these pages are trying to write to, make sure the anonymous user

(IUSR_MachineName) or the authenticated user(s) have access. For example, if you are trying to create a

Lotus.NotesSession object, the IUSR account needs write access to the Lotus\Notes\ folder.
If you are using Project Server, you might have the following errors:

File /<file>.asa Line <line> Out of memory. Unable to allocate required memory.

and

Active Server Pages, ASP 0100 (0x80004004)


Unable to allocate required memory.
/ProjectServer/Global.asa, line 18.

This is often caused by global.asa being 'scanned' by Anti-virus programs and other services that might modify

and/or lock various files on your server. See Q323019 for a description and possible workarounds.

If this error is happening from Outlook Web Access, you're probably getting one of the following errors (among

others):

A Active Server control or component performed an illegal


ole countinitialized call. Components used by Active Server Pages must
not do this.

or

File /<file>.asp Unexpected error.

You should be able to eliminate the error by reinstalling OWA and the latest Exchange service pack(s), though

sometimes it may require reinstallation of IIS as well. Also, see Q196016 (XWEB: Outlook Web Access Fails

Intermittently) and the OWA Troubleshooting Whitepaper.

If you are getting the following error:


The description for Event ID ( 5 ) in Source ( Active Server Pages )
could not be found. It contains the following insertion string(s):
File /<file>.asp Unexpected error.

or

File /<file>.asp Line 0 Out of memory. Unable to allocate required memory.

One possible kludge, or at least help in narrowing down the component causing the problem, is to increase the

application protection to high (isolated).

If you are getting the following error:

Error while reading default settings. please do regsvr32 asp.dll.

This message can be ignored (see Q290612), and re-registering asp.dll will probably not eliminate the error

message anyway.
How do I change a list into a set of table rows and columns?

759 requests - last updated Friday, June 14, 2002

Developers often have sets of things they want to present to a user, and the typical way is to process each element

and then put some kind of vertical space (e.g. <hr> or <p>).

Many times, these developers want an easy way to display more information without all the vertical space. Image

thumbnails, for example, that are 20 pixels wide, don't need their own 'row' in most HTML interfaces. So what we

want to do is say "let's display n of these per row, and when we reach n, start a new row." Here is a code sample

that will handle any number of elements, and any number of columns. Download the code and try it out; just change

the first two values and play with it. This code should be easy to adapt to your recordsets, array processing, etc.

<%
' number of elements, e.g. recordcount
numElements = 11

' # of columns in the table:


numCols = 5

' populate some dummy data


dim tmpArray()
redim tmpArray(numElements-1)
for k = 0 to numElements-1
tmpArray(k) = "Item " & cstr(k+1)
next

' all right, let's start the table


response.write "<table border=1>"

' enter the loop


for i = 0 to numElements-1
' control variable, to determine if we need
' to pad the last column(s) with space
needFooter = true
if i mod numCols = 0 then
' beginning of row
response.write "<tr>"
end if
' display each cell
response.write "<td>" & tmpArray(i) & "</td>"
if i mod numCols = numCols - 1 then
' end of row, so don't need to pad
response.write "</tr>"
needFooter = false
end if
next
if needFooter then
' okay, we need to put in the balance columns
response.write "<td colspan=" &_
numCols - (i mod numCols) &_
"> </td></tr>"
end if
response.write "</table>"
%>

If you were using recordset data, you would only adjust a few items. The following code assumes an open recordset

called rs, that is not EOF:

<%
numCols = 5

i = 0
do while not rs.eof
needFooter = true
if i mod numCols = 0 then
response.write "<tr>"
end if
response.write "<td>" & rs(0) & "</td>"
if i mod numCols = numCols - 1 then
response.write "</tr>"
needFooter = false
end if
i = i + 1
rs.movenext
loop
if needFooter then
response.write "<td colspan=" &_
numCols - (i mod numCols) &_
"> </td></tr>"
end if
response.write "</table>"
%>
How do I convert exchange rates in ASP?

748 requests - last updated Thursday, November 1, 2001

Here are three tools that will look up exchange rates in real-time, allowing you to perform conversions and other

calculations:

Cloanto Currency Server

IISCartEX

VoltoFXP

There is also an XML interface to Oanda's currency rates:

http://www.oanda.com/products/fxml/

As an alternative, while I don't advocate slurping from other sites, you could rig up the MSXML objects or some

other POST-emulating behavior to scrape from a site like http://www.xe.net/ucc/ or

http://finance.yahoo.com/m3?u.
Why does session.abandon not take effect right away?

704 requests - last updated Tuesday, April 16, 2002

You might notice that the results of the following code are quite peculiar:

<%
Response.Write Session.SessionID & "<BR>"
Session.Abandon
Response.Write Session.SessionID
%>

While one might expect that, immediately after a session is abandoned, a new session is created, in actuality the

first session is not really discarded until the current page goes out of scope... meaning the user will not get a new

SessionID until they access another ASP page in your application.


Why can't I retrieve custom header information from Request.ServerVariables()?

687 requests - last updated Monday, February 25, 2002

When you add custom headers through IIS or using Response.AddHeader, you might expect them to be available in

the ServerVariables collection. To understand why these values are not available to the requested ASP script, we

need to understand how HTTP connections to ASP pages evolve. First, the client makes a request for an ASP page.

At this point, it sends its request headers to the server, populating Request.ServerVariables() and other collections.

Once it has this information, IIS sends back a response, including standard and any custom headers. The HTML

page, or Excel spreadsheet, or ZIP is rendered to the client and/or offered for download. At no point are the headers

associated with the response object available via the request object.

Custom headers are for sending information to the browser, not for retrieving it. If you're sending custom headers,

e.g.:

<%
Response.AddHeader "blah", "blah"
%>

Then why would you need to do this:

<%
Response.AddHeader "blah", "blah"
Response.Write Request.ServerVariables("HTTP_blah")
%>

When you already know the content of the header? The above is logically the same as:

<%
Response.AddHeader "blah", "blah"
Response.Write "blah"
%>

See Q270645 for information from the source.


How do I embed a TAB character into source code?

683 requests - last updated Monday, July 8, 2002

Sometimes you want to make your HTML source code more readable, or insert consistent indents into non-HTML

elements like an e-mail. You can that with either of the two following commands:

<%
Response.Write(vbTab)
' or
Response.Write(CHR(9))
%>

To embed a TAB character into results coming out of SQL Server (e.g. for use in a tab-delimited text file based on a

recordset), you can do this:

SELECT 'Tab-->' + CHAR(9) + '<--Tab' FROM table

Keep in mind that this output will not look the same in HTML as it does in View Source, notepad or Query Analyzer.
Why do I get 'The RPC Server is Unavailable' messages?

657 requests - last updated Monday, March 4, 2002

This can happen if you have objects in application scope (even under MTS). This is why Article #2053 explains you

should avoid storing non-thread-safe objects in session or application variables. If you are using MTS, and believe

your object is safe to store in session or application scope, then make sure your package is set "Leave Running

When Idle" instead of "Timeout After Inactivity."

If you have a component that is running out of process in IIS 4.0, take a look at Q290822... you may be able to

obtain a hotfix from Microsoft if the problem can't be fixed with a service pack.

See Q265340 if you are attempting to run a DCOM application.

In Control Panel / Services, ensure that Remote Procedure Call (RPC) Service is set to startup automatically.

As with cookies / sessions not working, the fact that your development server has an underscore in the name might

be the cause. See if the errors go away when you access the machine by IP. If you have renamed your web server

(from or to any name, including names with underscores), see Q234142 for possible solutions.

Check the logs for any further info you might be able to obtain about this problem. You might see a web log entry

along the lines of:

Out of process ISAPI extension request failed 500

And a system event log entry like this:

Event ID 37
Out of process application '/LM/W3SVC/2/ROOT' terminated unexpectedly.

You can also poke around Q224370 for a potential problem residing outside of IIS entirely.

Of course the problem usually goes away by simply refreshing the page. If it doesn't, you can unload the application

/ web site in Internet Services Manager, or use iisreset from the command line to restart the entire web service.
You can also attempt to change the application protection level of the application / site temporarily, to see if this

resolves the problem. If you still get the problem repeatedly, you may have a corrupt mediabase. Try reinstalling IIS

(though only after you've exhausted all of the above), but keep in mind you will have to reconfigure plenty of

options if you go this route.


How do I convert numbers into words?

651 requests - last updated Monday, April 15, 2002

Here is an example submitted by Barry Camp:

<%
ss = "one,two,three,four,five,six,seven,eight,nine"
ds = "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen," & _
"seventeen,eighteen,nineteen"
ts = "twenty,thirty,forty,fifty,sixty,seventy,eighty,ninety"
qs = ",thousand,million,billion"

Function nnn2words(iNum)
a = split(ss,",")
i = iNum mod 10
if i > 0 then s = a(i-1)
ii = int(iNum mod 100)\10
if ii = 1 then
s = split(ds,",")(i)
elseif ((ii>1) and (ii<10)) then
s = split(ts,",")(ii-2) & " " & s
end if
i = (iNum \ 100) mod 10
if i > 0 then s = a(i-1) & " hundred " & s
nnn2words = s
End Function

Function num2words(iNum)
i = iNum
if i < 0 then b = true: i = i*-1
if i = 0 then
s="zero"
elseif i <= 2147483647 then
a = split(qs,",")
for j = 0 to 3
iii = i mod 1000
i = i \ 1000
if iii > 0 then s = nnn2words(iii) & _
" " & a(j) & " " & s
next
else
s = "out of range value"
end if
if b then s = "negative " & s
num2words = trim(s)
End Function

' let's kick the tires:

Response.Write num2words(0)&"<br>"
Response.Write num2words(196)&"<br>"
Response.Write num2words(789)&"<br>"
Response.Write num2words(-32768)&"<br>"
Response.Write num2words(999999)&"<br>"
Response.Write num2words(1000000)&"<br>"
Response.Write num2words((2^16)-1)&"<br>"
Response.Write num2words(2^16)&"<br>"
Response.Write num2words(2147483647)&"<br>"
%>

Here are some examples in T-SQL, some of which may need some modifications (and some of which will require SQL

Server 2000):

http://www.xephon.com/oldcode/Q014A05

httphttp://www.users.drew.edu/skass/sql/nameMoney.sql.txt

http://groups.google.com/groups?hl=en&selm=4bc601c193ca%242fd61280%249ae62ecf%40tkmsftngxa02

http://groups.google.com/groups?hl=en&selm=%23rkSX%23wUAHA.244%40cppssbbsa05
How do I generate a treeview from ASP?

622 requests - last updated Sunday, June 30, 2002

While we construct a useful and portable example, here are some relevant links:

CodeProject.com

comobjects.net

DarkFalz.com

obout.com

Planet Source Code

TreeGen.com

xefteri.com
Why do I get the error Object Required: ''?

600 requests - last updated Friday, September 6, 2002

When mixing code from several different sources, you may come across this error:

Microsoft VBScript runtime error '800a01a8'


Object required: ''
/<file>.asp, line <line>

This is usually because you've tried to close or set to nothing an object that hasn't been defined. To reproduce, try

this code:

<%
set conn = Server.CreateObject("ADODB.Connection")
conn.open <connection string>
' ...
objConn.close
set objConn = nothing
%>

Another possible cause is using a reserved object name as a variable name. For some reason, ASP lets you do this:

<%
dim response
%>

However you will get the above error if you do this:

<%
dim response
response.redirect("http://www.foo.com/")
%>
But oddly enough, not when you do this:

<%
dim response
response.write("http://www.foo.com/")
%>

The line number in the error message should make it fairly easy for you to track down which object's name you've

messed up. My advice is to always open late, close early... close your objects as soon as you're finished with them.

But if you have sloppy code that doesn't keep track of its objects, or you are using a catch-all at the bottom of the

page, how do you prevent these errors? You can check if the object exists by using the isObject() function:

<%
if isObject(objectName) then
set objectName = nothing
end if
%>

If the object has open/close methods, you can also check their state to see if they're open, and make sure that you

close them properly. This can be done, for example, with recordset and connection objects:

<%
const adStateOpen = 1
if NOT(rs IS NOTHING) then
if rs.state = adStateOpen then rs.close
set rs = nothing
end if
%>
How can I increase the amount of connections in Workstation / Professional?

600 requests - last updated Tuesday, October 30, 2001

NT 4.0 Workstation, Windows 2000 Professional and XP Professional all come with an inherent limit of 10

simultaneous connections. This is because it is a business / development operating system, not a Web server. This

OS should be used for testing your ASP applications, not deploying them or hosting them in the real world. You will

need to get a flavor of Server to do this...


Why do I get errors in the 800A0001 -> 800A000F range?

583 requests - last updated Tuesday, September 17, 2002

800A0001

Persits.Upload.1 error '800a0001'


Request.BinaryRead failed.

or

Persits.Upload.1 (0x800A0001)
Unspecified error

This is usually because you used request.form while also using ASPUpload. If you need to retrieve files *and*

incoming form variables, use upload.form in place of request.form.

800A0002

Persits.MailSender.4 error '800a0002'


Winsock error 11002 (0x2AFA) occurred.

Check that the server name specified in your .Host property is valid, and if it is a domain name, that it can be

resolved from the web server (not your workstation). As a workaround, use the IP address instead.

800A0003

Persits.Upload.1 error '800a0003'


Nothing has been posted.

Make sure your <FORM> is using the POST method. If you are trying to prevent this error during the first load of a

form that POSTs to itself, use the .IgnoreNoPost property (see PS01040429).
800A0004

Persits.MailSender.4 error '800a0004'


Connection refused.

Check the server name you are using is valid and populated in the .Host property, that the SMTP service is running

(and on the default port), and make sure it allows relaying (either anonymous or only from within the network).

If the SMTP service is running on a port other than 25, you can use the .Port property to specify the correct port

number. If your SMTP server requires outgoing authentication, you can use the .username and .password properties

(available with the premium version of ASPEmail).

Persits.MailSender.4 error '800a0004'


Address already in use.

This is a Winsock error in the ASPEmail component, but it is rare and has been difficult for the development team to

track down any details about it.

800A0005

Server object error 'ASP 0178 : 800a0005'


Server.CreateObject Access Error
/<file>.asp, line <line>
The call to Server.CreateObject failed while checking permissions.
Access is denied to this object.

or

Server object, ASP 0178 (0x80070005)


The call to Server.CreateObject failed while checking permissions.
Access is denied to this object.

You need to allow IUSR_machineName access to your project / DLL through DCOMCNFG. See Q255502 and, more
importantly, the workaround section of Q259725.

Microsoft VBScript runtime (0x800A0005)


Invalid procedure call or argument: <method>

See Article #2101 for a brief discussion of this specific error message.

Persits.Upload.1 error '800a0005'


Access is denied.

or

Persits.Upload.1 error '800a0005'


The system cannot find the path specified.

Make sure the folder you are defining in the .Save method exists, and that IUSR_machineName has write

permissions. If you are trying to upload to a network share, you can use the .LogonUser method (see PS01032618).

Persits.MailSender.4 (0x800A0005)
Software caused connect abort.

This can happen when the mail message you are sending is larger than the SMTP server allows. Send a smaller

message, or adjust the SMTP server's message size property.

800A0006

Microsoft VBScript runtime (0x800A0006)


Overflow: 'cint'

or

Microsoft VBScript runtime (0x800A0006)


Overflow: 'clng'
An integer in VBScript can only support up to the number 32,678. Any number larger than that will throw an

overflow error when trying to use Cint(); use CLng() instead, which has a much higher upper bound

(2,147,483,647). If you need even more than that, use CDbl(). Beyond that, convert to a string, because you

shouldn't be expecting to use that number for calculations anyway.

Persits.MailSender.4 (0x800A0006)
501 5.5.4 Invalid Address

Make sure your .From address is in proper format. Maybe you left out the @ symbol, or left the .From property

blank.

Persits.MailSender.4 (0x800A0006)
503 5.5.2 Need Rcpt command.

Make sure you are using AddAddress, AddCC or AddBCC at least once, and that they contain e-mail addresses in

proper e-mail format.

800A0007

Microsoft VBScript runtime error '800a0007'


Out of memory: <file>.asp

This is usually caused by creating a very large array - see Article #2196 for more information.

Persits.MailSender.4 (0x800A0007)
The system cannot find the path specified.

You are using the AddAttachment method, and passed in a blank path, a client-side path, or a mapped drive letter

path. Make sure the path is valid, IUSR_machineName has read access to the file and folder, and that you use UNC

paths if you need to attach files from network shares. To make sure IUSR_machineName has access to UNC paths,

see Article #2168.


800A0008

Persits.Upload.1 (0x800A0008)
Size of file <file> exceeds the maximum allowed size of <n> bytes.

You have uploaded a file that is larger than the .SetMaxSize property has been defined. Either increase the

.SetMaxSize value, upload a smaller file, or display a friendly error message (see PS01092671).

800A0009

Microsoft VBScript runtime (0x800A0009)


Subscript out of range: <variable>

This usually means you tried to reference an element of an array that is outside of the upper and lower bounds of

the array. See Article #2317 for more information.

800A000A

Microsoft VBScript runtime (0x800A000A)


This array is fixed or temporarily locked

If you trying to define an array's size dynamically after you have populated some elements in it, make sure you use

Redim Preserve. If you are changing the number of dimensions, do so BEFORE you populate any elements in the

array. If you are changing the upper bounds of individual elements, see Article #2067 for techniques for redeclaring

arrays using Redim.

800A000B

Microsoft VBScript runtime (0x800A000B)


Division by zero
Check that you only perform any division operations when you know the denominator will not be 0. For example:

<%
' ...
denom = clng(request.form("foo"))
if denom <> 0 then
quotient = num / denom
else
quotient = 0
end if
%>

800A000C

Microsoft VBScript runtime (0x800A000C)

Although our audience has searched for this error message many times, we have not been able to find any useful

information about this error on the web, in Microsoft's Knoweldge Base, or in the entire archived history of

newsgroups (according to Google). If you have any information about this error code, please let us know.

800A000D

Microsoft VBScript runtime (0x800A000D)


Type Mismatch: <variable>

This error usually comes because you have bad syntax which leads ASP to believe you are trying to use a function as

an array, or a string as a function. Here are a few examples that will cause this error:
<%
dim foo(5)
foo(0)

' or

foo

' or

var foo = "foo"


%>

In the first case, it looks like the code is attempting to call a sub or function called "foo" - when VSBcript is expecting

the code to asign a value to the first element of the array "foo." The second example looks like an attempt to call a

sub or function called "foo", when no such function exists. The last method "borrows" the keyword "var" from

J/JavaScript. Variable declaration does not use the "var" keyword in VBScript.

800A000E

Microsoft VBScript runtime (0x800A000E)


Out of string space

This can happen when you are trying to concatenate a large string together, and exceed the server's capacity to

hold your string in memory. To work around this, if you are writing to the screen, use multiple response.write()

statements instead of holding onto all of that string in the buffer and response.writing it in one line. It you are

writing to a text file, use multiple fs.writeline commands instead of one. You can demonstrate this error by running

the following code (WARNING: do not run on a production system!!!):


<%
server.scriptTimeout = 300000
for i = 1 to 10
str = str & string(32000000,".")
next
%>

Let it fly, and watch Task Manager until the error times out. In Windows 2000, memory usage for

inetinfo.exe/dllhost.exe should go through the roof (in .NET Server, watch w3wp.exe).

This error can also happen with arrays. If you are creating very large arrays because you don't know how many

elements will populate them, consider breaking the work across iterations of re-using a smaller array. Also, as soon

as you are done working with the array, empty it out by using the following command:

<%
Erase ArrayName
%>

This will free up the memory used by the array.

Persits.Upload.1 (0x800A000E)
Fatal error: can't find next separator.

This is a bug in IE 5.0 and/or ASPUpload 2.0. It is fixed by ASPUpload version 2.1.0.1, or 3.0. See PS01071762 for

more details.

800A000F

Microsoft VBScript runtime (0x800A000F)

As far as we can tell, this error is not in use and is reserved for "unknown" errors. If you have received this error,

please feel free to provide us details about the code that caused it, any text associated with the error code, and
what you may have done to resolve or work around the problem, and we will update this article with that

information, so other people might benefit from it...


Why can't I turn buffering off using Response.Buffer in IIS 5.0 and up?

554 requests - last updated Thursday, October 17, 2002

For several reasons, mostly performance and the ability to handle 500 errors more flexibly, buffering is enabled by

default in IIS 5.0, 5.1 and 6.0. In IIS 4.0, buffering was disabled by default, so you could enable it and disable it at

will (however countless people walked into the problem described in Article #2011). Now, when you create an ASP

page such as the following:

<%
Response.Buffer = False
' ...
%>

You will most likely see this error:

Response object error 'ASP 0157 : 80004005'


Buffering On
/<file>.asp, line <line>
Buffering cannot be turned off once it is already turned on.

When buffering is enabled by default, you cannot override it at page scope. To do so, you have to disable buffering

for the entire web site / application, then enable buffering in each page as required. To do this, open Internet

Services Manager, right-click the web site or application, and select properties. On the Home Directory tab, click the

Configuration button, and on the Options tab, uncheck "Enable Buffering"... but don't be surprised if the rest of your

app starts behaving weird.


How do I find out the amount of space left on my server?

551 requests - last updated Wednesday, February 20, 2002

In VBScript, you can do this:

<%
set fso = Server.CreateObject("Scripting.FileSystemObject")
for each drive in fso.Drives
cDriveName = fso.GetDriveName(drive)
set cDrive = fso.GetDrive(drive)
if cDrive.DriveType=2 then
response.Write cDriveName & " - " &_
formatnumber(cDrive.FreeSpace/1024/1024,0) &_
" MB free<br>"
end if
next
set fso = nothing
%>

In JScript, you can do this:

<script language=jscript runat=server>


var fso = new ActiveXObject("Scripting.FileSystemObject");
for (d = new Enumerator(fso.Drives); !d.atEnd(); d.moveNext())
{
if (d.item().DriveType==2)
{
Response.Write(d.item() + " - ");
var space = d.item().FreeSpace;
space = Math.round(space/1024/1024);
space = space.toLocaleString();
Response.Write(space.substring(0,space.length-3));
Response.Write(" MB free<br>");
}
}
var fso = null;
</script>

If you are concerned about the space on your SQL Server machine, you can do this:

EXEC master..xp_fixedDrives

Which will return a list like this:

drive MB free
----- -----------
C 2435
D 7596
E 6097
F 7892
G 7393

Note that this is an undocumented extended stored procedure, and should not be used in production code (since its

behavior may change in future versions). Another alternative, for a single drive, is to parse the results of the

following statement:

EXEC master..xp_cmdshell 'dir c:\'


How do I make sure the client is still connected before processing?

513 requests - last updated Monday, April 22, 2002

This is a very trivial example of using the Response.IsClientConnected property. Basically, you have a file where you

are doing a lot of processing, and you only want to continue if the user is still connected to the ASP page. You can

test this while the page is still executing; in the following example, we have one big loop, followed by another.

Before entering the second loop, we test if the client is still connected, and then again after the second loop. This is

just to populate the text file, to demonstrate how this works. Load this page once, and let it finish. Then, hit

Ctrl+Refresh and close the browser (or hit Stop) before the page finishes executing, and examine the different lines

in your log file.

<%
set fso = server.createobject("Scripting.FileSystemObject")
set fs = fso.openTextFile(server.mappath("/log.txt"),8,true)
fsStuff = now & " - gone during first loop"
for i = 1 to 1000000
boo = ""
next
if response.isClientConnected then
fsStuff = now & " - gone during second loop"
for j = 1 to 1000000
hoo = ""
next
if response.isClientConnected then
fsStuff = now & " - " & i+j
end if
end if
fs.writeline fsStuff
response.write fsStuff
fs.close: set fs = nothing
set fso = nothing
%>

It should be relatively easy to port this logic to your own code.

You might get this error:


Microsoft VBScript runtime error '800a01b6'
Object doesn't support this property or method: 'Response.IsClientConnected'

If so, you need to be using a newer version of IIS.


How do I perform a Whois / DNS lookup from ASP?

493 requests - last updated Wednesday, October 30, 2002

If you're like the rest of us, you don't want to re-invent the wheel or learn socket programming just to do a Whois or

nslookup query.

With a component:

ACIWhois

ASPWhois

ASPWhois II

C2GWhois

DesWhois

DNSQuery

DynuWhois

EasyWhois

ECS Whois

etiveWHOIS

HexTcpQuery

IP*Works

WebWhois

WhoisDLL
And without a component:

Web Wiz Guide

Planet Source Code


How do I change the default server scripting language in InterDev?

490 requests - last updated Thursday, November 1, 2001

In Visual InterDev 1.0, choose Options from the Tools menu, and then select the HTML tab. In the Default scripting

language area under Active Server Pages, select a language.

In Visual InterDev 6.0, right-click the name of the project in the Project Explorer, and then choose Properties.

Choose the Editor Defaults tab, and then under Default script language, select a new default.

In Visual InterDev.NET, there are two methods (though neither work for me yet):

(a) with an ASP file open, click View, Property Pages, and on the General tab there is a dropdown at the bottom for

server-side scripting language. When I attempt to change this to JScript, InterDev shuts down with a "Microsoft

Development Environment has encountered a problem and needs to close. We are sorry for the inconvenience." blah

blah blah...

(b) with an ASP file open, click View, Properties Window, and in that environment you'll see one of the properties is

'defaultServerScript' with a dropdown next to it. When I attempt to change this to JScript, I get the error "Invalid

Property Value: Value null was found where an instance of an object was required."

I'm running Beta 2 on XP Pro, and the RC on XP Advanced Server 3541. YMMV.
How do I pad digits with leading zeros?

487 requests - last updated Tuesday, September 17, 2002

Here is one way. Basically, it compares the number of desired digits with the length of the number being padded,

and creates that many instances of zero to its left.

<%
Function PadDigits(n, totalDigits)
if totalDigits > len(n) then
PadDigits = String(totalDigits-len(n),"0") & n
else
PadDigits = n
end if
End Function
%>

And in JavaScript:

<script language=javascript runat=server>


function PadDigits(n, totalDigits)
{
n = n.toString();
var pd = '';
if (totalDigits > n.length)
{
for (i=0; i < (totalDigits-n.length); i++)
{
pd += '0';
}
}
return pd + n.toString();
}
</script>

Sample usage:
<%
Response.Write(PadDigits(46,8)) ' returns 00000046
Response.Write(PadDigits(4,5)) ' returns 00004
Response.Write(PadDigits(32,6)) ' returns 000032
Response.Write(PadDigits(22,1)) ' returns 22
%>

And here are a couple of methods in T-SQL:

SELECT REPLACE(STR(column, 5), SPACE(1), '0') FROM Table

SELECT RIGHT('00000' + CAST(column AS VARCHAR(5)), 5) FROM Table

And here is a technique that can be useful for sorting on a varchar column that might contain numbers (you can't

order by CAST AS INT because some values will contain characters):

SELECT column FROM table ORDER BY LEFT('00000', 5-LEN(column)) + column

To see how this can change a query, consider the following example (SQL Server):

SET NOCOUNT ON
CREATE TABLE #foo(bar VARCHAR(5))
INSERT #foo VALUES('1')
INSERT #foo VALUES('2')
INSERT #foo VALUES('9')
INSERT #foo VALUES('10')
INSERT #foo VALUES('20')

-- compare the results of this query:


SELECT bar FROM #foo ORDER BY bar

-- to the results of this query:


SELECT bar FROM #foo ORDER BY LEFT('00000', 5-LEN(bar)) + bar
DROP TABLE #foo
How do I deal with disappearing application variables?

445 requests - last updated Thursday, August 15, 2002

Depending on your configuration, there can be many causes for application variables to suddenly become

unpopulated.

■ re-saving global.asa

■ loading / unloading the application or web site

■ rebooting the server / tripping on the power cord

■ stopping / starting the web service (W3SVC or IISADMIN)

■ issuing an errant Application.Contents.Remove() or .RemoveAll()

Now, if you have code in ASP pages that rely on these application variables being populated, you should have a

contingency plan. When confronted with this situation, I used a separate ASP page which could be hit directly to

regenerate the application variables from scratch. This file was also included in global.asa (to reduce redundancy

and ensure consistency), in the application_onStart event, as described in Article #2144. And to allow applications

to redirect to this paeg and then get sent back to the referer, it itself is also included in another ASP page.

Basically, it goes something like this. In your ASP page that *depends* on the application variables, you have this

logic:

<%
if Application("StringVariable") = "" then
response.redirect("/globalInclude.asp")
else
StringVariable = application("StringVariable")
end if
' ... continue page
%>

Then globalInclude.asp looks something like this:


<!--#Include file=global.asp-->
<script language=VBScript runat=server>
ref = Request.ServerVariables("HTTP_REFERER")
if ref <> "" then
Response.Redirect(ref)
else
Response.Redirect("/")
end if
</script>

And finally, global.asp looks like this:

<script language=VBScript runat=server>


' clean up any remaining bits
Application.Contents.RemoveAll()

' okay, now start populating the app vars


Application("StringVariable") = "foo"
' ...
</script>

In global.asa, of course, you would only include global.asp in the application_onStart() method, since otherwise it

would try to redirect, which is not allowed in global.asa.


Why do I get 8000FFFF / 8002802B errors?

434 requests - last updated Sunday, September 29, 2002

If you are getting this error:

ASP 0115 : 8000ffff


A trappable error occurred in an external object. The script cannot continue running.

...please see Article #2171

Otherwise, read on.

The 8000ffff error is actually quite common, and can have a variety of causes / resolutions. Outside of the trappable

error message, the most common err.description is 'Catastrophic failure'...

Make sure to use the latest version of MDAC (http://www.microsoft.com/data/). If you are using SQL Server, see

Q243349 for more information.

If you are using a DSN or otherwise ODBC-dependent connection string, use a DSN-less connection string (and in the

case of Access, use JET/OLE-DB instead of the generic Access driver). See Article #2126 for sample connection

strings.

Do not store connection or recordset objects in the session or application variables. See Article #2053 for more

information.

If you are using a server-side cursor with an ADODB.Recordset and a transaction, consider using a client-side cursor

(adUseClient) or using a default recordset against the connection object (see Q187942). Conversely, this error might

be caused by issuing an .update or .AddNew call against a default, forward-only recordset - in which case, you should

use an INSERT or UPDATE statement directly against the connection object.

If you are retrieving OLE, BLOB, TEXT or IMAGE columns, make sure they are left out of your resultset when they are
NULL. You can do this in the WHERE clause by stating:

WHERE blobColumn IS NOT NULL

...or alter the output, as per Article #2150

For SQL Server, if you are connecting using a SQL Server UID/PWD, make sure the database is set up for Mixed

Mode authentication, as opposed to just Windows authentication. Also make sure that the SQL Server service is

started, and that you are connecting to a valid instance name (if connecting to an instance other than the default).

If you are using Oracle oo4o (OracleInProcServer.XOraSession), you might see this error also:

Active Server Pages error '8002802b'


Create object failed

Make sure the oo4o libraries are installed on the server, and that you are connecting to the server correctly

(including identifying a user with appropriate permissions).

If you are using Commerce Server and/or BizDesk, you might see this error:

Active Server Pages (0x8002802B)


An error occurred while creating object 'g_MSCSConfiguration'.

or

Active Server Pages error '8002802b'


Create object failed
/<file>.asp, line <line>
An error occurred while creating object 'MSCSSite'.

This can happen if you've changed the password of the user who installed commerce server after it's been installed -

make sure under Component Services / Security that the components are set to the interactive user, not a specific
user account.

If you see an 8002802b error on any other COM object, make sure you are using set objectName =

Server.CreateObject("Valid.ProgID"). If you are using an <OBJECT Progid=> tag, this error can be more likely to

occur. Also, make sure you are not setting a COM object to a session variable unless it has been explicitly optimized

for doing so (see Article #2053). Make sure that the DLL(s) in question have been registered on the system ... try re-

registering them with regsvr32 <path>\file.dll. Next, check the permissions on the DLL - make sure 'everyone' (if

using Windows auth) or IUSR_<machineName> (if using anonymous access) has read and execute permissions. A

temporary workaround would be to set the IIS process isolation to low until you find the real solution.

If you are using Access, you might have seen one of the following:

Microsoft OLE DB Provider for ODBC Drivers error '8000ffff'


Query cannot be updated because the FROM clause is not a single simple table name.

or

Microsoft OLE DB Provider for ODBC Drivers error '8000ffff'


The query is not updateable because the from clause is not a single simple table name.

This is usually because you created an ADODB.Recordset, grabbed a query that JOINed two or more tables, and then

attempted to perform an AddNew or Update through the recordset object. To resolve this issue, use an INSERT or

UPDATE statement directly against the connection object, and if using SQL Server, use a stored procedure call.

If you are using JMail, this is the error code for just about every error there is. I'm going to list out a few of them,

and provide explanations and/or workarounds:

The message was undeliverable

Make sure the SMTP server is valid, and that it accepts mail relayed from your domain and/or address. You might

need to use a component that supports outgoing SMTP authentication, if your SMTP server is blocking relays. <--

FWIW, this is a good thing... as is anything that can reduce the number of SPAMs I get each day.
Cannot open file

Make sure that if you are using the AddAttachment method, you pass it a valid path and filename.

MergeMail: Interface Not Supported

The MergeMail feature in JMail is only offered in the registered version.


How do I redirect a user to https:// if they access a page with only http://?

385 requests - last updated Thursday, July 11, 2002

This should be placed at the top of any page that is accessible by http:// but that you want accessed only through

https://

<%
if Request.ServerVariables("HTTPS") = "off" then
srvname = Request.ServerVariables("SERVER_NAME")
scrname = Request.ServerVariables("SCRIPT_NAME")
response.redirect("https://" & srvname & scrname)
end if
%>
Why does IsNumeric() return true for some strings that contain characters?

376 requests - last updated Tuesday, November 26, 2002

VBScript's IsNumeric() function does not actually determine whether an expression contains only numeric digits; it

determines whether the expression could be evaluated as a number. In the following cases, isNumeric returns true:

<%
response.write isNumeric("3e4") & "<p>"
response.write isNumeric("3d4") & "<p>"
response.write isNumeric("&H05") & "<p>"
response.write isNumeric("$45,327.06") & "<p>"
%>

To see why, you should notice that d was once used to denote double precision, e is used to express scientific

notation, that &H is used to express a hex number, and of course $ , and . are used to express currency (other

currency symbols in other regional settings will be subject to the same problem). You will also see this problem with

numbers beginning with & (e.g. Octal numbers), numbers with parentheses (denoting negative), and numbers with

leading + and - signs.

Observe the following output:

<%
response.write 1 * 3e4 & "<p>"
response.write 1 * clng("3d4") & "<p>"
response.write 1 * &H05 & "<p>"
response.write 1 * cdbl("$45,327.06") & "<p>"
%>

(While the use of the d and e characters produce the same result, it should be noted that d requires explicit

conversion (e.g. to Long) to work correctly, while e is implicitly converted.)

So, to test if these 'numbers' really are numeric, one workaround is to write your own isNumeric() function that

simply steps through the

expression and tests each character (digits 0 through 9 have ASC values between 48 and 57), e.g.:
<%
function isReallyNumeric(str)
isReallyNumeric = true
for i = 1 to len(str)
d = mid(str, i, 1)
if asc(d) < 48 OR asc(d) > 57 then
isReallyNumeric = false
exit for
end if
next
end function

response.write isReallyNumeric("3e4") & "<p>"


response.write isReallyNumeric("3d4") & "<p>"
response.write isReallyNumeric("&H05") & "<p>"
response.write isReallyNumeric("$45,327.06") & "<p>"
' and a sanity check:
response.write isReallyNumeric("1234") & "<p>"
%>

You'll have to adjust if you want to accept certain characters as 'numeric', e.g. commas, decimal points, + and -

signs, etc.

Note that JScript/JavaScript's isNaN() function has similar limitations; it also sees 'e' as scientific notation, for

example.

T-SQL also has this problem (where D and E are interpreted as numbers), for example:

SELECT ISNUMERIC('0E30')

To get around this, you can wrap your own isnumeric function that uses the following logic:
CREATE FUNCTION isReallyNumeric
(
@num VARCHAR(64)
)
RETURNS BIT
BEGIN

RETURN CASE
WHEN PATINDEX('%[^0-9]%', @num) = 0 THEN
1
ELSE
0
END
END
GO

SELECT isReallyNumeric('0002')
SELECT isReallyNumeric('00E2')
SELECT isReallyNumeric('00F2')

Of course you could use the same kind of logic within T-SQL, e.g. in a WHERE clause or CASE expression, so you

don't have to use a user-defined function...


Why do I get 80020005 errors?

362 requests - last updated Monday, August 12, 2002

There are several variations of the 80020005 error. Here are a few of them:

Server object error 'ASP 0193 : 80020005' (or 'ASP 0193 : 80020009')
OnStartPage Failed
/<file>.asp, line <line>
An error occurred in the OnStartPage method of an external object.

This is typically an error coming from a custom COM object / component. One way around it is to re-write the object

using ObjectContext, instead of onStartPage / onEndPage (which are deprecated as of the release of Windows

2000).

Provider error '80020005'


Type mismatch.

This can happen if you try to set a date column to a value that is not in proper date format, e.g. "UPDATE tableName

SET dateColumn='31/31/2002'"... it can also happen when using a command object and named constants (e.g.

adDate and adLongVarChar); I will always recommend using a straight EXEC statement against a connection object,

rather than go through the hassle and rigorous code required by an ADODB.Command object.

Response object error 'ASP 0106 : 80020005'


Type Mismatch
/<file>.asp, line <line>
An unhandled data type was encountered.

This can happen if you try to response.write data coming out of a blob / ole / image column, or response.binarywrite

non-binary data. To get around the blob scenario, you can upgrade to the latest MDAC

(http://www.microsoft.com/data/), which allows you to avoid the GetChunk / AppendChunk methods. See Q258038

for (admittedly VB-based) samples of avoiding GetChunk and AppendChunk, and Q276488 for sample code using

ADODB.Stream to send binary data to the browser...


Another scenario is using a COM object to response.binarywrite large image data. For example, I've seen errors with

ASPImage when a JPG file was over 575kb (though I don't know why an image that big would ever be on the web,

much less in JPG format at all). One workaround would be to use a different object (maybe ADODB.Stream) to

output the file; another would be to use smaller files.

Finally, see Article #2099 if neither of the above situations applies to you.
How do I highlight words in a string?

345 requests - last updated Friday, August 9, 2002

Typically, this can be done simply using replace():

<%
' define the string to replace:
strR = "ho"

' define the string which contains the strR:


tStr = "fo bo ho yo wo fo ho fo zo" & _
" go bo ho fo ho ho yo to mo"

' replace the strR with <b>strR</b>:


tStr = replace(tStr, strR, "<b>" & strR & "</b>")
%>

One problem with this solution is that it is case sensitive. So, if you were to have multiple upper/lowercase versions

of the target word, it would only replace those that exactly match the case of your variable. The following example

takes a slightly different approach, by looping through the string and inserting the highlighting tags (in this case

<b></b>) at strategic points, including all case versions of the word.

<%
' define the string to replace:
strR = "ho"

' define the string which contains the strR:


tStr = "fo bo ho yo wo fo HO fo zo" & _
" go bo Ho fo hO ho yo to mo"

response.write "Original:<p>" & tStr & "<p>"

' keep the length of strR handy:


w = len(strR)

' loop through until strR is no longer in tStr:


do while instr(lcase(tStr), lcase(strR)) > 0

' get the current position of strR:


cPos = instr(lcase(tStr), lcase(strR))

' build the new string, maintaining case:


nStr = nStr & _
left(tStr, cPos - 1) & _
"<b>" & mid(tStr, cPos, w) & "</b>"

' take the searched part out of tStr:


tStr = right(tStr, len(tStr) - cPos - w + 1)

loop

' append the leftover fragment to the new string:


nStr = nStr & tStr

response.write "New & improved:<P>" & nStr


%>
How do I decode an encoded URL?

338 requests - last updated Tuesday, September 10, 2002

This question has been asked frequently (and variants of 'decode' keep showing up in this site's search logs). While

the Server object provides a Server.URLEncode() method, they didn't bother providing a Server.URLDecode() or

Server.URLUnEncode() method. I assume they didn't think it would ever be needed, but they were wrong... I've

been in a few situations where this function would have been very handy. Here are both URLEncode and URLDecode

functions (for completeness) in VBScript and JScript:

In VBScript:

<%
Function URLDecode(str)
str = Replace(str, "+", " ")
For i = 1 To Len(str)
sT = Mid(str, i, 1)
If sT = "%" Then
If i+2 < Len(str) Then
sR = sR & _
Chr(CLng("&H" & Mid(str, i+1, 2)))
i = i+2
End If
Else
sR = sR & sT
End If
Next
URLDecode = sR
End Function

Function URLEncode(str)
URLEncode = Server.URLEncode(str)
End Function

str1 = "http://www.foo.com/blah.asp?foo=1 & 2 &g=0"


str2 = URLEncode(str1)
str3 = URLDecode(str2)
Response.Write(str1 & "<br>" & str2 & "<br>" & str3)
%>

And here in JScript:

<script language=JScript runat=server>

function URLDecode(str)
{
return unescape(str);
}

function URLEncode(str)
{
str = escape(str);

// JScript doesn't think '/' needs to be escaped...


// I'm not sure it does either, but take it out to be
// consistent with VBScript's built-in URLEncode()

while (str.indexOf("/")!=-1)
{
str = str.replace("/","%2F");
}
return str;
}

var str1 = "http://www.foo.com/blah.asp?foo=1 & 2 &g=0";


var str2 = URLEncode(str1);
var str3 = URLDecode(str2);
Response.Write(str1 + "<br>" + str2 + "<br>" + str3)

</script>
How do I make hyperlinks out of plain text URLs and e-mail addresses?

336 requests - last updated Monday, August 19, 2002

Many times you will get data out of a database and expect the browser (like e-mail) to automatically make http://

URLs and mailto: addresses clickable. The browser doesn't work that way, but it is fairly trivial to work around when

the entire column is a hyperlink (see Article #2188). However, it is not so simple when the items you need clickable

are buried in the middle of other text. The following example shows how to highlight a body of text's *valid* links

(URLs, \share\s, and e-mail addresses) and should also prevent munging up links which already have correct HTML

formatting (if you find a scenario that falls through the validation, please let us know).

<%
' let's make a big fictitious paragraph, and pretend
' it came out of a database or other external source

oldParagraph = "here's a string with 2 urls http://www.aspfaq.com/ " & _


"and http://www.microsoft.com/sql/ and a file share for kicks " & _
"\share\file\ just to be complete. Also, let's throw a URL " & _
"that contains a 'http' string https://www.foo.com/http/ and a " & _
"URL that's already a link <a href='http://foo.com'>foo.com</a> " & _
"jusy for fun also, and an invalid URL http://a and another " & _
"file share, but this one not valid: \share and let's do " & _
"some e-mail addresses too, again just for fun. Here's " & _
"one: foo@foo.com and an invalid one: f@f@f.a and here's " & _
"a pre-built mailto: <a href=mailto:f@f.com>f@f.com</a> " & _
"some carriage returns: " & vbCrLf & "http://www.foo1.com/" & _
vbCrLf & "http://www.foo2.com/"

' let's display it to the screen, to show it is unformatted

response.write "OLD:<p>" & oldParagraph

' break the paragraph into words

words = split(trim(oldParagraph))

' loop through, to check each 'word'


for i = 0 to ubound(words)

' check that it starts with http:// or is a share

words(i) = fixLink(lcase(trim(words(i))))

next

' okay, join the paragraph back together and display

newParagraph = join(words)

if instr(newParagraph, vbCrLf) then

' need to check each carriage return


' not caught by above split

words = split(newParagraph, vbCrLf)

for i = 0 to ubound(words)

words(i) = fixLink(lcase(trim(words(i))))

next

newParagraph = join(words, vbCrLf)


end if

response.write "<hr>New:<p>" & newParagraph

function fixLink(w)
if _
(left(w,4) = "http" and len(w) > 11 and instr(w,"://") > 0) _
or _
(left(w,2) = "\" and len(w) > 3 and instrRev(w,"\") > 2) then

' looks like a valid link!


w = "<a href=" & w & ">" & w & "</a>"

' check that it is a valid e-mail address next


' (we don't check this FIRST, because a URL could
' contain an @ symbol

elseif _
(instr(w,"@") > 0 and len(w) > 4 and _
instr(w,"@") = instrRev(w,"@") and _
instr(w,"href=mailto:") <= 0) then

' looks like a valid e-mail address!

w = "<a href=mailto:" & w & ">" & w & "</a>"


end if
fixLink = w
end function
%>

This was a fairly recent question on the newsgroups, and I initially set out to solve the problem with a SQL Server

stored procedure. However, I thought it would be more useful to provide a VBScript example, then it wouldn't

matter if the data came out of Access, Excel, SQL Server, MySQL, or a text file. This finds URLs, e-mail addresses

and file share links that are preceded by a space or carriage return. If they are embedded in HTML, you will have to

add logic to handle that case.


Why do I get 80020009 errors?

333 requests - last updated Friday, September 27, 2002

error '80020009'
Exception occured

or

Microsoft OLE DB Provider for ODBC Drivers error '80020009'


Errors occurred

If you are using a MEMO, TEXT, or VARCHAR(>255), see Article #2188.

ADODB.Field error '80020009'


The object referenced by the application no longer points to a valid object.

This can often happen if you reference a recordset object that was created on a different page, or in session scope.

If the former, you will need to re-query the database for this recordset; if the latter, you should consider another

plan for implementation... you should never store a recordset object in session scope (see Aticle #2053).

Microsoft OLE DB Provider for ODBC Drivers error '80020009'


Multiple-step OLE DB operation generated errors. Check each OLE DB
status value, if available. No work was done.

See Article #2288 for a lengthy description of this error message.


ADODB.Field error '80020009'
Either BOF or EOF is True, or the current record has been deleted; the
operation requested by the application requires a current record.

or

No current record.

See Article #2246 for more information about this error message.

Server object error 'ASP 0193 : 80020009'


OnStartPage Failed
/<file>.asp, line <line>
An error occurred in the OnStartPage method of an external object.

Article #2330 has some information about this error message.

DAO.Workspace error '80020009'


ODBC--call failed.

Try to avoid using DAO, if possible. DAO isn't highly recommended for use from ASP, even through a DLL.

Microsoft VBScript runtime error '80020009'


Object required: '<object>'

See Article #2283 for more information about this message.


Microsoft Cursor Engine error '80020009'
The data provider or other service returned an E_FAIL status.

This could indicate an underlying problem with SQL Server, but is often an issue with the receiving script. Make sure

you are connecting with OLEDB (see Article #2126), have installed the latest MDAC

(http://www.microsoft.com/data/), and have the latest Service Pack for SQL Server (see Article #2151). For more

information, see Q249638.

Server object error 'ASP 0177 : 80020009'


Server.CreateObject Failed
/<file>.asp, line <line>
A call to Server.CreateObject Failed. The requested object
instance can not be created.

For more information on CreateObject failure, see Article #2388.

CDONTS.NewMail.1 error '80020009'


451:SMTP server didn't terminate session

or

501:SMTP server error

or

503 Application Restarting.

or

No Recipients.
First off, use CDO.Message instead (see Article #2026, under 'Windows XP', for an explanation).

<component> error '80020009'


Type mismatch

Make sure any variables you are passing between ASP <-> component are declared as variants within the DLL...
What could cause all of my session or application variables to disappear?

325 requests - last updated Thursday, August 15, 2002

Typically, all active sessions will be terminated if IIS is reset, the application is unloaded / re-created within Internet

Services Manager, or if the web server is rebooted (obviously).

In addition, this can happen when global.asa is changed. Not only when a human physically changes the file, but it

has been known to be triggered by FrontPage Server Extensions, Index Server, several varieties of anti-virus

software, and even file replication software.

If you are having trouble tracking down the event that caused the problem (e.g. in the Windows Event logs and IIS'

own log files), check the last modified date/time of global.asa, and see if it was changed at around that time. This

should at least give you a bit of a direction in finding out what happened.

For information on how to remedy the problem, see Article #2297


How do I convert a name to proper case?

312 requests - last updated Tuesday, June 18, 2002

Here is a function that will handle this conversion. It will work for normal names and concatenations, like O'Brien

and Moseley-Williams. It will not, however, work for joined names like VanWyck.

<%
Function ProperCase(strIn)
strOut = ""
boolUp = True
For i = 1 To Len(strIn)
c = Mid(strIn, i, 1)
if c = " " or c = "'" or c = "-" then
strOut = strOut & c
boolUp = True
Else
If boolUp Then
tc = Ucase(c)
Else
tc = LCase(c)
End If
strOut = strOut & tc
boolUp = False
End If
Next
ProperCase = strOut
End Function
%>

Sample usage:
<%
Response.Write(ProperCase("o'brien")) ' returns O'Brien
Response.Write(ProperCase("moseley-williAMS")) ' returns Moseley-Williams
Response.Write(ProperCase("VanWyck")) ' returns Vanwyck
%>
Why am I having problems installing Visual Studio.NET RTM?

309 requests - last updated Thursday, February 21, 2002

Rather than outline all of the potential hiccups, I'll point you to a verbose article about one man's experience with

upgrading Beta 2 -> RTM.

Guerilla Install Tactics for Visual Studio.NET Final

If that doesn't answer your question(s), you can probably go to the vstudio setup group for help:

microsoft.public.vstudio.setup

FWIW, I installed the Enterprise Architect version on three machines. One was XP Pro, one was .Net Enterprise

Server 3590, and the last was .Net Enterprise Server 3604. All had beta versions of .Net installed, and even though

I did not follow the advice of the readme file to the letter, I haven't had any problems on any of the machines.
Why do I get 'Object doesn't support this property or method' errors?

276 requests - last updated Monday, August 19, 2002

Microsoft VBScript runtime error '800a01b6'


Object doesn't support this property or method: '<property or method>'
/<file>.asp, line <line>

Usually, this is because you used a property or method that doesn't exist, or doesn't exist in the version of the

object you're using. For example, if you have MSXML 3.0 installed, and you try this code:

<%
Set xmlhttp = Server.CreateObject("MSXML2.ServerXMLHTTP")
xmlhttp.setRequestHeader "User-Agent","foo"
%>

Or if you are running IIS 4.0, and you try this code:

<%
Server.Transfer "foo.asp"
%>

(See Article #2136 for more information on this scenario)

And finally, if you tried to assign a variable to your own 'version' of an intrinsic object, e.g.:

<%
response = "foo"
%>
Can I mimic VBScript's trim, ltrim, rtrim in server-side JScript / JavaScript?

275 requests - last updated Friday, July 19, 2002

Here is a sample that uses regular expressions to replicate the functionality of VBScript's *trim functions in

JavaScript:

<script language=jscript runat=server>

function ltrim(str)
{
return str.replace(/^[ ]+/, '');
}

function rtrim(str)
{
return str.replace(/[ ]+$/, '');
}

function trim(str)
{
return ltrim(rtrim(str));
}

var foo = " bar ";

Response.Write("_" + ltrim(foo) + "_<br>\n");

Response.Write("_" + rtrim(foo) + "_<br>\n");

Response.Write("_" + trim(foo) + "_");

</script>

While the HTML might look like it trimmed too much in the first two cases, if you view the *source* of the output,

you will see that all of the spaces are intact.


Why am I getting 'subscript out of range' errors?

274 requests - last updated Monday, July 8, 2002

Subscript out of range usually means you tried to access an element of an array that was either greater than its

ubound or lower than its lbound. The following error occurs:

Microsoft VBScript runtime error '800a0009'


Subscript out of range: '[number n]'
/<file>.asp, line <line>

Here is a demonstration of the problem:

<%
str = "hello,there,you"
strs = split(str,",")
response.write strs(12)
%>

Looking at the code, it's easy to see that the highest element of the strs() array would be 2 (since split() returns a 0-

based array). To prevent the error from happening, always consult the ubound of an array before blindly trying to

access it. For example:

<%
str = "hello,there,you"
strs = split(str,",")
if ubound(strs) >= 12 then
response.write strs(12)
else
response.write "Sorry, there is no 12th element."
end if
%>
Why do I get 'A script block cannot be placed inside another script block' errors?

267 requests - last updated Wednesday, May 15, 2002

You might have seen this error when using ASP to generate client-side script:

Active Server Pages, ASP 0138 (0x80004005)


A script block cannot be placed inside another script block.

This probably happened because you did this:

<script language=vbscript runat=server>


Response.Write "<script>alert('foo');</script>"
</script>

Or this:

<script language=jscript runat=server>


Response.Write("<script>alert('foo');</script>");
</script>

One way to alleviate this is to use the @language directive and use <%%> delimiters throughout:

<% @language="vbscript" %>


<%
Response.Write "<script>alert('foo');</script>"
%>

Or this:
<% @language="jscript" %>
<%
Response.Write("<script>alert('foo');</script>");
%>

If you are using both languages in the same page, you may have to use this alternative workaround, where you

break the script tags up:

<script language=vbscript runat=server>


Response.Write "<scr" & "ipt>alert('foo');</scr" & "ipt>"
</script>

Or this:

<script language=jscript runat=server>


Response.Write("<scr" + "ipt>alert('foo');</scr" + "ipt>");
</script>
How do I get the server's timezone information?

267 requests - last updated Sunday, July 21, 2002

You will likely need to call these pages as a user other than the anonymous IUSR. This is because extra privileges

are required for reading the registry and/or WMI. Here is a method for obtaining the information from the registry:

<%
set oShell = server.createobject("WScript.Shell")
stn = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\" & _
"Control\TimeZoneInformation\StandardName"

atb = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\" & _


"Control\TimeZoneInformation\ActiveTimeBias"

response.write oShell.RegRead(stn) & "<br>"


response.write "(currently " & oShell.RegRead(atb)/60 & _
" hours off GMT)<br>"
%>

The following WMI script should work on Windows 2000 and up -- not sure about NT 4.0. Note that you may have to

parse the output, as you probably get more than you want...

<%
Set Locator = Server.CreateObject("WbemScripting.SWbemLocator")
Set NameSpace = Locator.ConnectServer()
WQLQuery = "SELECT caption,daylightBias FROM Win32_TimeZone"
Set TZCollection = NameSpace.ExecQuery(WQLQuery, "WQL", 48)
For Each Item In TZCollection
response.write Item.caption & " (if daylight time, add " & _
Item.daylightBias/60 & " hours to the abs(offset).)"
next
%>

Instead of calling this dynamically, you could use a scheduled VBS file to run every day, or every week, to check the

current timezone and store this information in a flat file or a database (after all, how often does the time zone
change?). That way you only have the 'hit' to the registry / WMI so often, and you don't have to worry about the

permissions of IUSR.
How do I prevent 'Invalid use of Null' errors?

263 requests - last updated Thursday, June 27, 2002

When working with database or array values, you may have seen this:

Microsoft VBScript runtime error '800a005e'


Invalid use of Null: 'Replace'
/<file>.asp, line <line>

To alleviate this error, instead of this:

<%
x = replace(rs("x"), something, somethingElse)
%>

Do this:

<%
rsx = rs("x")
if len(rsx) = 0 then
x = ""
else
x = replace(rsx, something, somethingElse)
end if
%>

For database values, try preventing NULLs from coming out of the database in the first place. See Article #2073 and

Article #2150 for more information.


Can I have optional parameters to my subs / functions?

260 requests - last updated Saturday, August 10, 2002

Unlike VB, VBScript does not support the OPTIONAL keyword, so you cannot set up a function to conditionally accept

parameters. There are some kludges though, such as:

1. pass in a delimited string, with required parameters first, e.g.:

<%
function test(foobar)
foos = split(foobar,",")
response.write foos(0)
if ubound(foos)>0 then response.write foos(1)
end function

test("1,2")
test("1")
%>

2. pass in empty strings or other token values that tell the sub or function to ignore that value, e.g.:

<%
function test(foo,bar)
response.write foo
if bar <> "" then response.write bar
end function

test("1","2")
test("1","")
%>

JScript, on the other hand, allows you to pass optional arguments, as demonstrated in the following code snippet:
<script language=JScript RUNAT=SERVER>
function test(foo,bar)
{
Response.Write(foo);
Response.Write(bar);
}

test('1','2');
test('1');
</script>
How do I solve 'The Requested Resource is in Use' errors?

252 requests - last updated Monday, August 12, 2002

Check your code, make sure all objects are closed and set to nothing. This includes any command or ADOX objects

using an Active.Connection property, make sure this property is set to nothing as well.

If you are using thisPage.NavigateURL method, use response.redirect or server.transfer instead - avoid the thisPage

object.

Install the latest service packs and security releases (see Article #2151), and make sure you have the most recent

MDAC refresh (http://www.microsoft.com/data/).

If you have 'Cache ASP applications' checked, try unchecking it temporarily (see Q182059 for more information).

Try changing the address space and/or isolation settings for the application / site in question.

Try dropping and re-creating the application.

In addition to these suggestions, if you are getting Event ID 36 in your event log, see Article #2226.

If you are getting 'The RPC Server is Unavailable', make sure you are not accessing a machine with an underscore in

the name, and otherwise follow the suggestions in Article #2147 -- most importantly to restore the IWAM account.
Why do I get ASP 0113 / Script timed out errors?

248 requests - last updated Saturday, August 17, 2002

If you have some longer pages, usually with database activity, you may have come across this error:

Active Server Pages error 'ASP 0113'


Script timed out
<file>.asp, line <line>
The maximum amount of time for a script to execute was exceeded. You
can change this limit by specifying a new value for the property
Server.ScriptTimeOut or by changing the value in the IIS
administration tools.

A 'bandaid' is to increase the value for Server.ScriptTimeout (see Article #2066). The default value is 90 seconds, so

something in your script must be taking longer than 90 seconds. (I do not recommend doing this for your entire site,

as the error message suggests, as this may mask other problems for you.)

Now that you've identified a page that takes longer than 90 seconds to run, you need to work on it, rather than

simply relying on the bandaid. If you have nested recordsets, consider a JOIN. If your SQL queries take too long to

run, consider indexes, using GetRows(), and moving the queries themselves to stored procedures. If you are using

ADODB.Recordset or ADODB.Command to execute stored procedures or INSERT/UPDATE/DELETE rows in a table,

consider using the connection object by itself. If your page is 500 lines long, consider optimizing the code and

perhaps spreading the work over multiple pages. If you post your code to microsoft.public.inetserver.asp.general,

there will be people there who will help you optimize your code.
Why do I get 80010108 errors?

236 requests - last updated Wednesday, August 21, 2002

There are a variety of error messages that go along with the 80010108 error code.

Method '~' of object '~' failed

See Q270589 if you are using an array of dictionary objects.

See Q271461 if you are using an ADO Data Control (ADODC).

The object invoked has disconnected from its clients

Can be caused by using Server.CreateObject("InternetExplorer.Application") automation object to navigate to a web

site. See Article #2173 for sample code that uses XMLHTTP (MSXML) instead - this code is much more flexible, and

less prone to automation errors.

Also, see Q297218 if you are using Crystal Reports.

Other 80010108 errors

See Q172210 if you are using a VB component that accesses the NT Event Log.
How do I make sure an entered string contains only valid characters?

218 requests - last updated Wednesday, September 4, 2002

Often you are relying on users to enter valid text for element names, file names, folders, etc. There is no

"isValidFolderName()" function attached to the FileSystemObject, and even if there were, it would be useless

because this status changes with each new Windows version (for example, I can currently name a folder with a

comma in it, but Server.MapPath can't handle it).

The following technique will show you how you can easily adjust one line of code to prevent any of a list of

characters from appearing in a string.

<%
' here is the element name you want to check
' this can come from request.form or querystring

elementName = "foobar"

' control variable

invalcount = 0

' set up a list of unacceptable characters


' this includes spaces, dashes and underscores
' you can leave these out of the list
' you may need to add other characters, e.g. copied from MSWord

invalidList = ",<.>?;:'@#~]}[{=+)(*&^%$£!`¬| -_"

' check for " which can't be inside the string

if instr(elementName,chr(34))>0 then
invalcount = 1
else
' loop through, making sure no characters
' are in the 'reserved characters' list

for i = 1 to len(invalidList)
if instr(elementName,mid(invalidList,i,1))>0 then
invalcount = 1
exit for
end if
next
end if

if invalcount > 0 then


response.write "bad elementName."
else
response.write "good elementName."
end if
%>
How do I convert from Hex to Decimal and back?

204 requests - last updated Monday, July 29, 2002

Here are a couple of snippets that will help you convert a hexadecimal number to decimal, and vice-versa.

VBScript has a built-in dec->hex function called hex. To do the opposite, you use a concatenation of "&h" and the

hex value.

<%
Response.Write "<hr>Dec -> Hex, VBScript<p>"
DecVal = 16777215
Response.Write Hex(DecVal)

Response.Write "<hr>Hex -> Dec, VBScript<p>"


HexVal = "FFFFFF"
Response.Write CLng("&H" & HexVal)
%>

And here it is in JScript, which uses toString (with a radix of 16) to get hex from decimal, and parseInt which has

the ability to determine the integer value of a hex:

<script language=jscript runat=server>

Response.Write("<hr>Dec -> Hex, JScript<p>");


DecVal = 16777215;
Response.Write(DecVal.toString(16).toUpperCase());

Response.Write("<hr>Hex -> Dec, JScript<p>");


HexVal = "FFFFFF";
Response.Write(parseInt("0x"+HexVal));

</script>

This task is a little more daunting in T-SQL, and I hope to have a tested and working example available shortly.
How do I print the first n characters of a large block of text?

200 requests - last updated Wednesday, September 4, 2002

You can print, say, the left 200 characters or so of a block of text as follows:

<%
bigtext = "Hello, this is a big paragraph of text, used" & _
" solely for demonstration purposes. The code" & _
" will return the first 200 characters or so" & _
" to simulate some kind of ""excerpt"" where" & _
" you would later click for more info..."

excerpt = left(bigtext, instrRev(LEFT(bigtext, 200), " ")-1) & "..."

response.write excerpt
%>

If you want to add a space before the "...", either take out the -1, or add a leading space, e.g. " ..."

Having said that, if this information is coming from a database, you might want to adjust your query to be more

efficient; for example, only returning the first 200 or so characters across the wire (this is especially true for TEXT

columns, which often contain far more data than you would ever need to show in an excerpt... so why return it?).

SQL Server doesn't support an InstrRev-like function, so the query is a little more complicated than it might

otherwise be:
SELECT
excerpt = SUBSTRING
(
column,
1,
200-CHARINDEX
(
' ',
REVERSE
(
SUBSTRING
(
column,
1,
200
)
)
)
)+'...'
FROM
table

In Access, the query is a LOT more like the VBScript version, since Access supports VBA:

SELECT
excerpt = LEFT
(
column,
INSTRREV
(
LEFT
(
column,
200
),
" "
)-1
) & "..."
FROM
table

However, you might find that this query doesn't work from ADO (see Article #2394). If this is the case, then just

take the left 250 characters or so, and then apply the same logic in your ASP code as above. Please let us know if

you have problems implementing any of these suggestions.


Why do I get 80090016 errors?

197 requests - last updated Monday, July 8, 2002

You may have seen one of the following two errors:

Keyset does not exist


or
An error occurred reading or setting a configuration parameter

This error is usually due to permissions problems or, in rare instances, corruption of the metabase. Here are some

possible solutions:

■ try removing / re-creating the affected web site(s) / application(s)

■ if your application is running in an isolated process, try disabling this feature

■ also try running the application 'out of process'

■ try adding IWAM_MachineName to the web site operators, and giving this account full permissions (using

regedt32.exe) to

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineKeys\MS IIS DCOM Client

■ open regedit.exe, navigate to

HKEY_CURRENT_USER\Software\Microsoft\Cryptography\UserKeys

...and delete the subkey 'MS IIS DCOM Client', then open the MMC console for IIS (which will re-create the

subkey)

■ if all else fails, try reinstalling IIS - this will rebuild your metabase, but will require you to configure your

web site(s) from scratch


Why do I get errors in the 800A0400 -> 800A0415 range?

195 requests - last updated Monday, September 16, 2002

Microsoft VBScript compilation (0x800A0400)


Expected statement

This is usually due to using a With statement on a server with an older version of the script engines. Make sure you

have the most recent script engines on your server; specifically, With requires version 5.0. See Article #2151 for

information about getting the most recent updates.

Microsoft VBScript compilation (0x800A0401)


Expected end of statement

This is usually due to embedding double quotes inside a string, e.g.

strMyName = "foo "bar" jones"

To fix, either double-up the quotes, or use single quotes (see Article #2065 for more information):

strMyName = "foo ""bar"" jones"

or

strMyName = "foo 'bar' jones"

Microsoft VBScript compilation (0x800A0402)


Expected integer constant
This is due to attempting to use a variable to declare the bounds of an array. For more information, see Article

#2067.

Microsoft JScript compilation (0x800A0406)


Conditional compilation is turned off

This is caused by using a variable name that starts with an @ symbol. This is not a problem in VBScript, as it does

not try to interpret the @ symbol as a conditional statement. Here is an example that reproduces the problem:

<Script language=javascript runat=server>


Response.Write(@foo);
</script>

However we have been unable to find any documentation on enabling conditional compilation. :-(

Microsoft VBScript compilation (0x800A0408)


Invalid character

This is fairly easy to reproduce:

<%
Response.Write("foo"$)
%>

Obviously, that dollar sign doesn't belong there. See Article #2376 for a more detailed description of this error

message, and other possible causes.


Microsoft VBScript compilation (0x800A0409)
Unterminated string constant

This is usually caused by missing end quotes, for example:

strMyName = "foo 'bar' jones

Microsoft VBScript compilation (0x800A040D)


Invalid use of 'Me' keyword

'me' is a special word. See how the following script fails:

<%
set you = nothing
set me = nothing
%>

Try to avoid using 'me' for class and object names.

Microsoft VBScript compilation (0x800A040E)


'loop' without 'do'

This is often due to leaving out a do while not rs.eof, or finishing a while struct with a loop. It can also be caused by

improper nesting, e.g.


<%
i = 1
do while i < 10
if i = 5 then
loop
end if
%>

Microsoft VBScript compilation (0x800A040F)


Invalid 'exit' statement

You probably did something like this:

<%
for i = 1 to 10
' some logic
exit do
next

or

do while i < 10
' some logic
exit for
loop
%>

Obviously, the fix is to exit the right type of struct:


<%
for i = 1 to 10
' some logic
exit for
next

or

do while i < 10
' some logic
exit do
loop
%>

If you are using a while...wend struct, you might notice that this doesn't work:

<%
while i < 10
' some logic
exit while
wend
%>

Consider using a do while...loop struct instead (which is preferred anyway), or taking a different approach:

<%
while i < 10
' some logic
i = 10
wend
%>

Setting i to 10 will kick you right out of the while...wend.


Microsoft VBScript compilation (0x800A0410)
Invalid 'for' loop control variable

This can happen if you use the same variable name in a nested loop, e.g.

<%
for i = 1 to 5
for i = 1 to 10
' do something
next
next
%>

Microsoft VBScript compilation (0x800A0411)


Name redefined

This means you defined (using the dim keyword) a variable or array twice. This could be from including the same file

twice, or simply having two different dim statements for the same variable name. You will have to search the file for

the previous occurence, as the error message will point at the later line.

Microsoft VBScript compilation (0x800A0412)


Must be first statement on the line

This can happen when you use the following statement:


<%
If SomeCondition Then DoSomething End If
%>

An if/end structure should look like this:

<%
If SomeCondition Then
DoSomething
End if
%>

Which is easier to read than the correct version of shorthand, putting it all on one line:

<%
If SomeCondition Then DoSomething
%>

Notice that when you put the entire conditional on one line, the End if no longer belongs.

Microsoft VBScript compilation (0x800A0414)


Cannot use parentheses when calling a Sub

For information about this error, see Article #2115.

Microsoft VBScript compilation (0x800A0415)


Expected literal constant

You tried to assign a variable to a CONST statement. For example:


<%
CONST varName = someOtherVarName
%>

CONST should be used to declare literal strings or numeric values.

<%
CONST strVarName = "foo"
CONST intVarName = 4
%>

If you are re-assigning a variable's value to another value, use a regular variable instead of a CONST declaration.
How do I parse the domain name out of a URL?

189 requests - last updated Monday, September 9, 2002

This has come up several times. People have complex URLs with querystring parameters and different protocols

(e.g. http:// and https://) stored in a database, flat file, or user-entered, and they want to return just the domain

name portion of the URL. Split() is a wonderful function:

<%
Function ParseDomainFromURL(url)
urlParts = split(url,"/")
ParseDomainFromURL = urlParts(2)
End Function

complexURL = "http://foo.com/whatever.asp?foo=1&bar=2"
domain = parseDomainFromURL(complexURL)
response.write "Domain = <b>" & domain & "</b>"
%>

This function assumes that you are going to pass a valid URL, with a // preceding the domain name. You can check

for this first by testing within the function:

<%
Function ParseDomainFromURL(url)
if instr(url, "//") > 0 then
urlParts = split(url,"/")
ParseDomainFromURL = urlParts(2)
else
ParseDomainFromURL = "Invalid URL"
end if
End Function
%>

Note that we don't use a split on the double // character sequence, because splitting on single / allows us to isolate

just the domain name in one step, and also allows us to avoid having to special-case the scenario where the URL

doesn't have any other slashes (e.g. 'http://www.foo.com').


Why do I get ASP 0101 errors?

183 requests - last updated Tuesday, August 20, 2002

Request object error 'ASP 0101 : 80004005'


Unexpected error
/<file>.asp, line <line>
The function returned |.

This usually occurs with upload components, and can happen when you upload files larger than they can support.

This usually is around 2 MB, depending on the component.

If you are writing your own upload script / component, consider using BinaryRead in 'chunks' -- this way, you can

write out to your own buffer file as you receive each chunk (so you don't overwhelm the server with a huge in-

memory file), and before you read each segment, check if the client is still there by testing

Response.isClientConnected.

Some components seem to have no upper bound on file size, except the server's inability to hold the file in the

buffer, or the browser's inability to run a script for as long as it would take to transfer). If you believe you are

exceeding IIS' ability to buffer files, you can try to increase this limit (see Q260694. If that doesn't work, then try a

different component. ASPUpload, for example, has no problems with files much larger than 2 MB (though according

to PS01041741, you'll want to make sure you're using version 3.0 or higher).

Request object error 'ASP 0101 : 80070057'


Unexpected error
/<file>.asp, line <line>
The function returned |.

If you are using Sybase, see Q198943 for information on resolving 80070057 issues.

If you are using Informix, make sure you have the latest drivers. You can review IBM's documentation on how to get

these drivers installed (let us know if you can find a download, we couldn't!), or you can purchase from DataDirect.
Response object error 'ASP 0101 : 80020008'
Unexpected error
/<file>, line <line>
The function returned |.

If you are using Oracle, the error above probably means the Response object is trying to deal with a datatype that it

doesn't know how to handle. Verify that your query works outside of an ASP environment, and check the datatype of

each column that is coming back in the resultset. A way to quickly narrow down the column(s) causing the problem

is to alter your SELECT query to only select one column at a time... keep changing this column until you reproduce

the error (and we already know you're not using SELECT *, right?).
Why do I get 80070057 errors?

178 requests - last updated Wednesday, August 14, 2002

There are several variations of the 80070057 error. Here are the ones we've read about or experienced:

Server object error 'ASP 0177 : 80070057'


Server.CreateObject Failed
/secure/rcm/thanks.asp, line 23
The operation completed successfully.

This can happen when you use a named constant for FileSystemObject values, e.g.

<%
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objFILE = objFSO.OpenTextFile("c:\boot.ini", ForReading)
%>

Since VBScript (unlike VB) doesn't know what ForReading is, the FSO object doesn't know what to do, so it returns

an illogical error message. Here are the constants you will want to use in the OpenTextFile method:

ForReading 1

ForWriting 2

ForAppending 8

Active Directory error '80070057'


One or more arguments are invalid.

This can happen if you are creating ADSI code and you forget to include a proper value. For example, leaving out

the user name in a user query, or botching the WinNT: prefix.


Microsoft OLE DB Provider for ODBC Drivers error '80070057'
The parameter is incorrect.

This can happen if you use ad* constants, like adOpenSchema, without having ADOVBS.INC included. See Article

#2112 for details on using these constants without ADOVBS.inc.


Why does GUID not work correctly with response.write?

175 requests - last updated Wednesday, August 14, 2002

Here is a pretty simplistic usage of a GUID:

<%
set typ = server.createobject("Scriptlet.TypeLib")
Response.Write typ.guid & "foo"
set typ = nothing
%>

Notice that 'foo' is nowhere to be found on the page. For some reason, the GUID string comes with two extra

characters, and they get 'swallowed up' by the response.write call. I believe this is a bug in either the Win32

CoCreateGUID API, or how the scriptlet type library implements that call. I have sent in a report to Microsoft about

this issue; in the meantime, there are at least two workarounds. One is to manually strip off the offending

characters, and the other is to Server.HTMLEncode the string (which essentially does the same thing).

<%
set typ = server.createobject("Scriptlet.TypeLib")
guidStr = cstr(typ.GUID)

' strip off the last two characters:


Response.Write LEFT(guidStr), LEN(guidStr)-2) & "foo"

' encode the string:


Response.Write Server.HTMLEncode(guidStr) & "foo"
set typ = nothing
%>

Note that using CSTR and/or assigning the string to a local variable does nothing, on its own, to alleviate the

problem. Also, this isn't solely an ASP issue... the behavior occurs in ASP.NET as well.
How do I parse the file name out of a path or URL?

171 requests - last updated Monday, September 9, 2002

For a local path:

<%
path = "C:\inetpub\wwwroot\default.asp"
parts = split(path,"\")
response.write parts(ubound(parts))
%>

For a URL, there is only one slight change:

<%
path = "http://localhost/default.asp"
parts = split(path,"/")
response.write parts(ubound(parts))
%>

Keep in mind that if the path or URL doesn't have a file attached to it, you will get incorrect results. However, if the

path is ONLY a file name, it will work fine (in other words, your file name doesn't HAVE to be part of a path or URL).
How do I make sure my servers have the same time?

165 requests - last updated Monday, September 16, 2002

Often it is a concern that the system clocks on your web server(s) and/or database server(s) might go out of sync

after varying amounts of time. If your servers are not in sync, it might be difficult to analyze log traffic, errors, and

other system events.

Windows has been known to lose clock ticks based on how heavily the server is being bombarded by CPU-intensive

tasks. The best way to ensure that your servers keep consistent time is to use an authoritative time server, such as

tock.usno.navy.mil. For information on setting up your servers to synchronize with a military clock, see Q216734

(Windows 2000) and Q314054 (Windows XP/.NET).


Why do I get 800A0401 errors?

154 requests - last updated Friday, August 16, 2002

This error is encountered quite often by VB programmers venturing into VBScript. In VB, you can say this:

Dim i As Integer
For i = 1 to 10
Debug.Print i
Next i

In VBScript in an ASP page, on the other hand, both the Dim line and the Next line will cause the following error:

Microsoft VBScript compilation error '800a0401'


Expected end of statement
/<file>.asp, line <line>

To correct:

Make sure you only Dim variables in ASP, without using AS. VBScript only supports variants, so there is no need to

support explicit typecasting.

Make sure you just use 'Next' instead of 'Next i'... VBScript does not need to keep track of the control variable

(though my preference would be for it to just ignore such a statement, so that VB and VBScript would be that much

more syntactically consistent).

Make sure you don't create a string like this:

myString = ""stuff"

This will cause the error as well, since there are too many quotes.

Make sure you don't mix JavaScript syntax into a VBScript page, e.g.:
<script language=vbscript runat=server>
var x = "1"
</script>

(In newer versions of ASP, this actually produces a '0x800A000D Type Mismatch' error.)

Basically, look at the line of code the ASP error points to, and try to figure out if there's any syntax on that line that

you copied from another environment (e.g. VB), from another machine with possibly a newer version of ASP, or

from memory. Chances are, you copied or wrote code that won't work in your environment, or has a syntax error of

some kind.
How do I convert old IDC / HTX pages to ASP?

144 requests - last updated Wednesday, July 17, 2002

Microsoft produces a tool for this, which can be found at the following URL:

http://www.microsoft.com/ntserver/nts/downloads/archive/IISIDCHTXtoASP/default.asp

Make sure you read the read me page before diving in!
Why do I get 80029c84 errors?

137 requests - last updated Thursday, July 18, 2002

Microsoft VBScript Runtime Error '80029c84'


Circular Dependency Between Types/Modules

In newer versions of IIS, the error looks like this:

Active Server Pages error 'ASP 0135'


Cyclic Include
/<file>.asp, line <line>
The file '<file>.asp' is included by itself (perhaps indirectly). Please check include files
for other Include statements.

This is not a common error, but can be caused by creating #include files that #include each other (either directly or

in a more round-about way). Try to isolate / eliminate your #include files one by one from the calling page, or copy

all of the code from your include files into a single document to try and narrow down where this kind of situation

might be coming from. If the logic in each #include file is self-enclosed, you can try to hit each one in the browser

individually, and see if you can quickly find out which one will cause this error.
Why do I get 80070034 / 80070035 errors?

132 requests - last updated Sunday, August 25, 2002

Microsoft VBScript runtime error '80070035'


The network path was not found.

If you are connecting to a network server, make sure your NetBios name is 'clean' - e.g. no periods or underscores.

Make sure IUSR_machineName has access to the share / ADSI object you are trying to access. As a test, change the

web server's anonymous account to a domain user with the ability to log on interactively to the machine hosting the

share or object.

If you are using Site Server, and trying to connect to Novell shares, make sure you have Gateway Services for

Netware installed (see Q169269).

If you are trying to connect to a server's printers folder using ADSI, and are connecting to the server by name, try

connecting by IP address instead, or by only using the "true" host name of the computer (e.g. don't use an alias in

your HOSTS file). See Q252416 for more information. You can also employ the following workaround:

1. Open the file %WINDIR%\WEB\PRINTERS\IPP_001.asp

2. Find the following line:

If strComputer = "localhost" Or strComputer = "127.0.0.1" Then

3. add an additional OR clause for each alias for that machine, e.g.

If strComputer = "localhost" Or strComputer = "127.0.0.1" or _


strComputer = "Alias1" or strComputer = "Alias2" or _
strComputer = "204.17.34.21" or strComputer = "www.myserver.com" Then

4. open the file %WINDIR%\WEB\PRINTERS\IPP_004.asp and follow the same procedure.

If you are attempting to get to a user object by GetObject("WinNT://servername/username,user") syntax, try


GetObject("WinNT://servername/DomainControllerName/username,user") syntax instead.

If you are attempting to use secure authentication while connecting via ADSI's OpenDSObject, try temporarily

changing the fourth parameter to 0 (simple auth) instead of 1 (secure auth).


Why do I get 8002000E errors?

128 requests - last updated Monday, August 19, 2002

Response object error 'ASP 0185 : 8002000e'


Missing Default Property
/<file>.asp, line 0
A default property was not found for the object.

This can happen when you try to response write an object, e.g.

<%
' ...
set rs = conn.execute("SELECT column FROM table")
response.write(rs)
%>

Obviously, you'll want to loop through the resultset and response.write each element within it; you can't

response.write an object that way.

Session object error 'ASP 0185 : 8002000e'


Missing Default Property
/<file>.asp, line <line>
A default property was not found for the object

This can happen if you create an object, and then assign it to a session variable without using the SET keyword,

e.g.

<%
set objDict = Server.CreateObject("Scripting.Dictionary")
session("objDict") = objDict
%>
To resolve, use the SET keyword in the assignment of the object (but you shouldn't be storing objects in the session

anyway; see Article #2053.

Server object error 'ASP 0193 : 8002000e'


OnStartPage failure

This can happen in a custom COM object when using the onStartPage method, which has been deprecated in favor

of ObjectControl_Activate.
Why do I get 'Invalid Default Script Language' errors?

127 requests - last updated Sunday, August 18, 2002

You may have come across this error:

Active Server Pages error 'ASP 0201'


Invalid Default Script
The default script language specified for this application is invalid.

or

Active Server Pages error 'ASP 0129'


Unknown scripting language
The scripting language '<lang>' is not found on the server.

or

HTTP/1.1 Invalid Default Script Language

First, make sure that the page you're trying to load doesn't have a typo, e.g. <% @language=VBSkirt %>.

You can start by the helpful advice in Q296626. Make sure your default scripting language is set to a valid option;

most commonly, this is VBScript, but could be JScript or other languages. If you are going to change the default

scripting language, make sure *everyone* involved with the server knows this change is going to take place!

If that doesn't help, you'll want to download the latest scripting engine from Microsoft, at

msdn.microsoft.com/scripting/.
Why do I get 800A01F4 errors?

126 requests - last updated Saturday, August 17, 2002

If you use Option Explicit, you've probably seen this error:

Microsoft VBScript runtime error '800a01f4'


Variable is undefined: '<variablename>'

This usually means you created a variable somewhere in your script, but did not define it with a dim statement. Most

commonly, this encompasses the ADO constants, such as adOpenForwardOnly and adLockOptimistic. This involves

forgetting to include ADOVBS.inc or an alternative, which you can read about in Article #2112. If you don't define

those constants and you remove Option Explicit, you will get the following error (as described in Article #2102):

ADODB.Connection error '800a0bb9'


The application is using arguments that are of the wrong type, are out of
acceptable range, or are in conflict with one another.

If you are using ThisPage from Visual InterDev's PageObject, see Q190938 for information on resolving the issue

(though my advice is to avoid 'thispage' altogether).


Why do I get 80020003 errors?

125 requests - last updated Friday, November 8, 2002

'ASP 0185 : 80020003'


Missing Default Property

This error can happen when setting a barebones custom COM object to session or application scope. You shouldn't

be storing any objects in session or application scope, unless they are explicitly marked as safe for doing so. See <a

href=/2053>Article #2053 for more information.

This can also happen when you try and work with an object when you should be working with one of its properties,

e.g. the following:

<%
response.write response
%>

Generates this error:

Response object error 'ASP 0185 : 80020003'


Missing Default Property
/<file>.asp, line 0
A default property was not found for the object.
Why do I get errors in the 800A0030 -> 800A003A range?

123 requests - last updated Monday, September 23, 2002

There weren't as many error codes in this range as I had initially thought. Please let us know if you find others.

Microsoft VBScript runtime (0x800a0030)


Error in loading DLL

See Article #2357 for more information.

Microsoft VBScript runtime error '800a0033'


Internal error

This usually happens in global.asa, and indicates that the scripting engines have become corrupt. See Article #2151

for information on ensuring that your server is up to date.

Microsoft VBScript runtime (0x800A0034)


Bad file name or number

or

Server object error 'ASP 0177 : 800a0034'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

See Article #2379 for more information about these errors.


Microsoft VBScript runtime (0x800A0035)
File not found

or

Server object error 'ASP 0177 : 800a0035'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

See Q276011. If you are trying to access a file on a network drive, see Article #2168.

Microsoft VBScript runtime (0x800A0036)


Bad file mode

or

Server object error 'ASP 0177 : 800a0036'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

This usually means you tried opening or creating a file with a named constant, like ForAppending, when you should

have used a constant. The following chart shows the possible values:

ForReading 1

ForWriting 2

ForAppending 8
Microsoft VBScript runtime (0x800A003a)
File already exists

or

Server object error 'ASP 0177 : 800a003a'


Server.CreateObject Failed
/<file>.asp, line <line>
The operation completed successfully.

Sounds like you used Scripting.FileSystemObject to move or copy a file to a location, however there is already a file

with the same name at that location.


Why do I get 800A0408 errors?

118 requests - last updated Monday, August 19, 2002

Microsoft VBScript compilation error '800a0408'


Invalid character

If you cut and paste code from other sources (e.g. web sites, other editors, etc) you often bring along characters

that don't show up in Notepad but are, nonetheless, present -- or do appear as non-prinatable characters, that look

like little squares. If you're looking at the line in question and it isn't simply an unclosed string or a premature

carriage return, try deleting the line(s) altogether and re-typing them by hand. This should eliminate the possibility

of 'invisible' problem characters mucking up the stream.


Why do I get ASP 0130 / ASP 0131 errors?

117 requests - last updated Sunday, September 15, 2002

Active Server Pages error 'ASP 0130'


Invalid File attribute
/<file>.asp, line <line>
File attribute '/<file>.asp' cannot start with forward
slash or back slash.

Active Server Pages error 'ASP 0131'


Disallowed Parent Path
/<file>.asp, line <line>
The Include file '../<file>.asp' cannot contain '..' to
indicate the parent directory.

To work around either error, use a virtual include, with relative references from the root - rather than the current

directory, e.g.:

<!--#include virtual=/folder/file.asp-->

As far as the disallowed parent path goes, there is a security measure in IIS, designed mainly for multiple websites

(e.g. at an ISP). The concept is to prevent ASP pages in site A from including ASP pages from site B. This measure is

called 'enable parent paths' and is usually disabled.

Another workaround is to enable parent paths. You can find this setting in the IIS MMC, right-click the web site in

question, select properties, on the Home Directory tab, click configuration, and move to the options tab:
However this is the least preferred method. See Q226474 and Q184717 for details.
Why do I get 800A03EC errors?

103 requests - last updated Friday, September 20, 2002

Microsoft JScript compilation error '800a03ec'


Expected ';'

This usually means you left an invalid character at the end of a line, e.g.:

var str = "Foo.bar")

There are a few other errors, typically involving Microsoft Office or custom / 3rd party objects.

Microsoft Excel error '800a03ec'


Unable to set the LeftMargin property of the PageSetup class

For information about this error, see Q184291.

Microsoft Office Web Components 9.0 error '800a03ec'


The file <file>.gif could not be opened for export.

This typically means a permissions problem ... make sure IUSR_MachineName has write permissions on the target

folder.

<object> error '800a03ec'


Method '~' of object '~' failed

Here are some KB articles that might help with this error:

Q158997, Q193339, Q253661, Q255986, Q255733, Q264701, Q270589, Q292744

And if you really want to automate Office from ASP, you should read Q257757.
Why do I get 800A03F6 errors?

101 requests - last updated Saturday, August 17, 2002

If you are used to programming in JScript or T-SQL, you've probably seen this error once you've picked up

VBScript:

Microsoft VBScript compilation error '800a03f6'


Expected 'End'

This is often due to code snippets like the following:

<%
if foo then
response.write "foo"
else if bar then
response.write "bar"
end if
%>

If you are using an else if construct, keep in mind that it is one word. So the above should be:

elseif bar then

Another example is short-cutting a single if block, much like you can do in other languages:

<%
if foo then
response.write "foo"
%>

If you are creating an if structure that only has one possible outcome, you should handle it in either of the following

formats:
<%
if foo then
response.write "foo"
end if

' or

if foo then response.write "foo"


%>

Note that if you only have one branch and you need to commit multiple statements, and wish to do it on one line,

you can do so using a line separator (colon) -- though for readability it isn't usually recommended:

<%
if foo then response.write "foo": call somesub(): bar = false
%>
Why do I get 800A01C2 errors?

100 requests - last updated Wednesday, September 4, 2002

I think we've all seen this one:

Microsoft VBScript runtime error '800a01c2'


Wrong number of arguments or invalid property assignment: '<object.method>'

This can happen when trying to call a method like a property, or vice-versa; or by calling a method of a built-in

object, and passing in too many arguments.

However, the most common cause I've seen for this is using ADODB.Recordset to create a new record, and then

have something like this:

<%
' ...
RS.Fields.Item("ColumnName") = "someString"
' ...
%>

To resolve, you have one of two options. The first is to use an INSERT statement directly against a connection

object; this is both more efficient and less error-prone. The second is to use either of the following syntax styles:

<%
' ...
RS.Fields("ColumnName") = "someString"

' or

RS.Fields.Item("ColumnName").Value = "someString"
' ...
%>
Why do I get 800A138F errors?

98 requests - last updated Monday, August 19, 2002

Microsoft JScript runtime error '800a138f'


'<variable>' is not an object

Microsoft JScript runtime error '800a138f'


Object expected

Microsoft JScript runtime error '800a138f'


'<variable>' is null or not an object

Microsoft JScript runtime (0x800A138F)


'<variable>' is null or not an object

For starters, of course, make sure you are referring to an object that exists. If you are trying to access the

Application object, for example, and you spell it wrong, you're going to be out of luck. Make sure any custom

functions you are calling are in the script, or that the include file they're in is actually being included.

When using both languages in an ASP page, you can get this error if you try, from server-side JScript, to call a client-

side VBScript sub or function. See Article #2045 for details on mixing server-side scripting languages, because in

certain scenarios, your function call in one language will occur before the function even exists in the other language.

Do not use CreateObject by itself in JScript. Use one of the following types of syntax:

<script language=javascript runat=server>


var obj = new ActiveXObject("Prog.Id");
// or
var obj = Server.CreateObject("Prog.Id");
</script>
Why do I get 80010105 errors?

98 requests - last updated Saturday, September 21, 2002

Server object, ASP 0177 (0x80010105)


The server threw an exception.

or

Error type: (0x80010105)


Method '~' of object '~' failed

If you are attempting to send mail with CDO / CDONTS, you might find that .eml / .rtr files are sitting around in your

queue folder (usually indicating that your SMTP server is not configured correctly). See Article #2268 for more

information.

If you are attempting to automate a Microsoft Office product (Word, Excel, etc.), see Q257757... there may be some

advice there that will (perhaps indirectly) help resolve this issue. For example, you shouldn't attempt to set a Word

object to visible from within ASP, since the server-side isn't going to open an instance of Word for the system

administrators to see. Also, you should attempt to open an Office document from the server, by sending a

querystring over 255 characters. Use POST to send data to a page that is generating a Word or Excel document that

is going to open right into the user's browser.


Why do I get ASP 0158 errors?

96 requests - last updated Monday, August 19, 2002

Response object error 'ASP 0158 : 80004005'


Missing URL
/<file>.asp, line <line>
A URL is required.

or

Response object, ASP 0158 (0x80004005)


A URL is required.
/<file>.asp, line <line>

This is usually caused by an empty parameter to the Response.Redirect call. Instead of the following code:

<%
Response.Redirect(variable_that_holds_URL)
%>

Use the following code, temporarily, to debug:

<%
Response.Write(variable_that_holds_URL)
Response.End
%>
Why do I get 80070056 errors?

94 requests - last updated Sunday, September 15, 2002

When using LDAP / ADSI from ASP, you may have come across this error:

Active Directory Error '80070056'


The specified network password is not correct

First off, make sure you are using the correct password to connect to directory services. Next, make sure you are

using proper credentials (e.g. you have disabled anonymous access to the folder housing the ASP page(s)).
Why do I get 800A01F9 errors?

93 requests - last updated Saturday, September 21, 2002

Microsoft VBScript runtime (0x800A01F9)


Invalid or unqualified reference

This is usually due to using shorthand syntax (usually found inside a With...End With construct), e.g.:

<%
set fso = server.createobject("scripting.filesystemobject")
.deleteFile("c:\foo.txt")
set fso = nothing
%>

Should be

<%
set fso = server.createobject("scripting.filesystemobject")
fso.deleteFile("c:\foo.txt")
set fso = nothing
%>

or

<%
set fso = server.createobject("scripting.filesystemobject")
With fso
.deleteFile("c:\foo.txt")
End With
set fso = nothing
%>

Check if you have any method or properties that start with a dot (search the code for " ." without the quotes).
Why do I get 8004E00F errors?

91 requests - last updated Monday, August 19, 2002

You can get these errors when running SyncIWAM.vbs (usually as a result of Event ID 36, see Article #2226). You

can also get this error when running Commerce Server.

The reason for the error is that the distributed transaction coordinator (MSDTC) service is not started. To resolve

this issue, run the following from a command line:

net start MSDTC

If it is not set to start automatically, you should do so in Control Panel or Administrative Tools / Services.
Why do I get 800A01CA errors?

91 requests - last updated Monday, August 19, 2002

Microsoft VBScript Runtime error '800a01ca'


Variable uses an Automation type not supported in VBScript

If you are returning a numeric value from a database, make sure you convert it to a long or double before

attempting to use it for calculations (see Q195180).

If you have a custom COM object, make sure you are passing valid OLE automation datatypes back and forth (e.g.

those that can be supported as VARIANTs under VBScript). If you are using a BSTR as an [out] parameter, for

example, you might come across this error. One workaround would be to use an [out,retval] parameter instead.

You might have a simple typo, e.g. the following code would produce this error:

<%
response.write "foo = " &
rs("foo")
%>

Obviously, it seems like you are trying to call a method rs("foo") when it is in fact returning a string. Of course,

those two strings should either be on the same line, or concatenated by a proper line delimiter, e.g.:

<%
response.write "foo = " & rs("foo")

' or

response.write "foo = " & _


rs("foo")
%>
How do I count the number of times x occurs in string y?

4 requests - last updated Saturday, December 14, 2002

Basically, all you have to do is convert both strings to lower case, and compare the length of the original string with

the length of that string where the string to find has been removed. In VBScript:

<%
x = "one"
y = "two one two two one two two one two"
interimString = replace(lcase(y), lcase(x), "")
response.write((len(y) - len(interimString)) / len(x))

' another way:

x = "one"
y = "two one two two one two two one two"
response.write ubound(split(lcase(y), lcase(x)))
%>

In T-SQL:

DECLARE
@x VARCHAR(10),
@y VARCHAR(64)

SELECT
@x = 'one',
@y = 'one two two two one two two one two'

SELECT
(DATALENGTH(@y) - DATALENGTH
(
REPLACE(LOWER(@y), LOWER(@x), '')
))
/ DATALENGTH(@x)

Vous aimerez peut-être aussi