Say you need to store a lot of application configuration data in the system registry. Why would you need to do this, when you can use various .NET .config files?
The idea may seem kind of archaic, but you may work with web servers that have been around a long time, where legacy ASP and ASP.NET applications cohabitate. Different applications that live on these servers were probably written by different groups separate by geography and time. Standards become important in such an environment! Registry use is not an uncommon standard.
Okay, that's the long scenario explanation... Should you need to make repeated trips to a registry key or its subkeys, here is a C# class to facilitate. The functions make use of the static Microsoft.Win32.Registry object.
Example of usage:
Friday, March 30, 2007
Thursday, March 29, 2007
CDOSYS/CDOSYS - an Email Function that Works with Either
Say you have an extremely OLD classic ASP application, written circa 1999. Your company's CIO has mandated that all applications be migrated to new (Windows 2003/IIS6) servers.
Stuff that worked in IIS4-5 doesn't always work in IIS6. Emailing using the old CDONTS.NewMail object is trouble.
To prepare for the migration, you want a single code base that works on the old and the new server. The following functions , based on the "SERVER_SOFTWARE" server variable, will help.
Stuff that worked in IIS4-5 doesn't always work in IIS6. Emailing using the old CDONTS.NewMail object is trouble.
To prepare for the migration, you want a single code base that works on the old and the new server. The following functions , based on the "SERVER_SOFTWARE" server variable, will help.
Wednesday, March 28, 2007
Internet Explorer Web Controls - Simple TreeView Implementation
Wiring up a TreeView component can be tricky the first time, particularly if you've never seen it done. The following is a simple example of a Treeview hierarchy with multiple, external XML sources.
Code moved to Live Spaces blog for readability.
Here is the result:
Code moved to Live Spaces blog for readability.
Here is the result:
Tuesday, March 27, 2007
IT Jargon - "Time Stamp"
Time Stamp - to create some meaningless electronic record, usually an email, to prove that you showed up to work today. The record does not represent any real artifact of work or problem solved; it simply proves to anyone keeping track that you bothered to log in. Example of a meaningless time stamp-type message:
Wow, that was a helpful nugget! We sure are lucky to have this guy on our team. He's always on the lookout for new developments.
"Time Stamping" is a behavior common in matrixed, distributed organizations. E.G., you work for a technical department with say, 100 "resources", 1-2 directors who actually sign off on budgets and merit increases, and many project managers with some accountability and little authority.
For the "resources" in such an organization, it can be easy to hide and live by "time stamping" while working from home, shopping online and engaging in self-abuse. Look around, you probably know some "Time Stampers". Or maybe you are one. If so, good for you! Hopefully you are saving the money you are stealing, because the free ride won't last forever.
"Time Stamping" is a behavior common in matrixed, distributed organizations. E.G., you work for a technical department with say, 100 "resources", 1-2 directors who actually sign off on budgets and merit increases, and many project managers with some accountability and little authority.
For the "resources" in such an organization, it can be easy to hide and live by "time stamping" while working from home, shopping online and engaging in self-abuse. Look around, you probably know some "Time Stampers". Or maybe you are one. If so, good for you! Hopefully you are saving the money you are stealing, because the free ride won't last forever.
Monday, March 26, 2007
C# Console App to Update Machine.config
I store proprietary information in our company's web servers' Machine.config's in order to manipulate the behavior of multiple applications. E.G., an appSetting determines whether the current machine is used for shared development, integration, pre-production testing, or production.
Unfortunately, I am not an administrator on the servers. No, we have outsourced that privilege to a vendor without accountability. Sound like an accident waiting to happen? That's a story for another day...
Anyway I can send a ticket to their help desk to have someone add the <appsettings> section if it doesn't exist, and then add my "Environment" key. But how do I trust that the person who picks up the ticket knows the first thing about where the appSettings section goes within Machine.config or its case-sensitive nature? I CAN'T.
The following is code for a console app that will update Machine.config according to your command line arguments:
Code snippet moved to my Windows Live Spaces Blog, for better readability.
Unfortunately, I am not an administrator on the servers. No, we have outsourced that privilege to a vendor without accountability. Sound like an accident waiting to happen? That's a story for another day...
Anyway I can send a ticket to their help desk to have someone add the <appsettings> section if it doesn't exist, and then add my "Environment" key. But how do I trust that the person who picks up the ticket knows the first thing about where the appSettings section goes within Machine.config or its case-sensitive nature? I CAN'T.
The following is code for a console app that will update Machine.config according to your command line arguments:
Code snippet moved to my Windows Live Spaces Blog, for better readability.
Friday, March 23, 2007
IE Web Controls - Tree View - Client Scripts
The IEWC Tree View component is useful if you are writing an ASP.NET 1.1 application and need to display a relatively large collapsible hierarchy (tree structure).
During installation (addressed in an earlier blog), a "/webctrl_client" directory is added to your default web site (c:\inetpub\wwwroot\webctrl_client). By default, the treeview component uses this location to reference the client-side scripts for image rendering and click behavior.
If you have a shared server environment with multiple web sites (Win2k Server or Win2k3 server), you have to copy this webctrl_client folder to the home directories of each site using the controls.
Alternatively, you can define a custom location in your web.config. This would allow you to set up a virtual directory and maintain the webctrl_client stuff in a SINGLE PLACE.
Here is close to the syntax to add to your web.config, directly within the section.
<configSections>
<section name="MicrosoftWebControls" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
</configSections>
<MicrosoftWebControls>
<add key="CommonFiles" value="/iistreeprint.net/webctrl_client/1_0/" />
</MicrosoftWebControls>
During installation (addressed in an earlier blog), a "/webctrl_client" directory is added to your default web site (c:\inetpub\wwwroot\webctrl_client). By default, the treeview component uses this location to reference the client-side scripts for image rendering and click behavior.
If you have a shared server environment with multiple web sites (Win2k Server or Win2k3 server), you have to copy this webctrl_client folder to the home directories of each site using the controls.
Alternatively, you can define a custom location in your web.config. This would allow you to set up a virtual directory and maintain the webctrl_client stuff in a SINGLE PLACE.
Here is close to the syntax to add to your web.config, directly within the section.
<configSections>
<section name="MicrosoftWebControls" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
</configSections>
<MicrosoftWebControls>
<add key="CommonFiles" value="/iistreeprint.net/webctrl_client/1_0/" />
</MicrosoftWebControls>
Thursday, March 22, 2007
Wednesday, March 21, 2007
Random Google Tip
The following Google Query will search for browsable directories on the internet containing music:
-inurl (htm|html|php) intitle:"index of" +"last modified" +"parent directory" +description +size +(wmamp3) "Beatles"
If you have the time or inclination, this could be useful. Thanks to straight-A for pointing this out.
Enjoy
-inurl (htm|html|php) intitle:"index of" +"last modified" +"parent directory" +description +size +(wmamp3) "Beatles"
If you have the time or inclination, this could be useful. Thanks to straight-A for pointing this out.
Enjoy
Sorting Arrays in VBScript - Part 2
Yesterday, I included a function to sort an array of numbers or strings. The following is a minor spin on that function, sorting an array of OBJECTS by their .name attribute. Pretty much all Microsoft-related objects have this name attribute. So you could sort an array of website objects, virtual directory objects, file objects, etc.
Hope this helps somebody!
'******************************************************************************************
' SortObjectArrayByName - sort an array of objects by their name attributes
' arrObj - incoming array of unsorted objects
'******************************************************************************************
function SortObjectArrayByName(arrObj)
dim i, j, temp
dim name0, name1, obj
for i = UBound(arrObj) - 1 To 0 Step -1
for j= 0 to i
set obj = arrObj(j)
name0 = obj.name
set obj = arrObj(j+1)
name1 = obj.Name
if name0 > name1 then
set temp = arrObj(j+1)
set arrObj(j+1) = arrObj(j)
set arrObj(j) = temp
end if
next
next
SortObjectArrayByName = arrObj
end function
Hope this helps somebody!
'******************************************************************************************
' SortObjectArrayByName - sort an array of objects by their name attributes
' arrObj - incoming array of unsorted objects
'******************************************************************************************
function SortObjectArrayByName(arrObj)
dim i, j, temp
dim name0, name1, obj
for i = UBound(arrObj) - 1 To 0 Step -1
for j= 0 to i
set obj = arrObj(j)
name0 = obj.name
set obj = arrObj(j+1)
name1 = obj.Name
if name0 > name1 then
set temp = arrObj(j+1)
set arrObj(j+1) = arrObj(j)
set arrObj(j) = temp
end if
next
next
SortObjectArrayByName = arrObj
end function
Tuesday, March 20, 2007
Sorting Arrays in VBScript
Here's a simple function to sort your simple array. Hope it makes your life simpler.
'****************************************************************************************** ' SortArray - sort a string or numeric array
'****************************************************************************************** function SortArray(arrShort)
dim i, j, temp
for i = UBound(arrShort) - 1 To 0 Step -1
for j= 0 to i
if arrShort(j)>arrShort(j+1) then
temp=arrShort(j+1)
arrShort(j+1)=arrShort(j)
arrShort(j)=temp
end if
next
next
SortArray = arrShort
end function
'****************************************************************************************** ' SortArray - sort a string or numeric array
'****************************************************************************************** function SortArray(arrShort)
dim i, j, temp
for i = UBound(arrShort) - 1 To 0 Step -1
for j= 0 to i
if arrShort(j)>arrShort(j+1) then
temp=arrShort(j+1)
arrShort(j+1)=arrShort(j)
arrShort(j)=temp
end if
next
next
SortArray = arrShort
end function
Internet Explorer Web Controls - Installation
Here is an obscure, simple install package for the IE Web Controls, useful in ASP.NET 1.1:
http://www.microsoft.com/downloads/details.aspx?FamilyID=FAC6350C-8AD6-4BCA-8860-8A6AE3F64448&displaylang=en
It mentions that the controls are specifically for Content Management Server. Don't be fooled. They are the same controls. The CMS team seems to have taken it upon themselves to create a more user friendly install package.
http://www.microsoft.com/downloads/details.aspx?FamilyID=FAC6350C-8AD6-4BCA-8860-8A6AE3F64448&displaylang=en
It mentions that the controls are specifically for Content Management Server. Don't be fooled. They are the same controls. The CMS team seems to have taken it upon themselves to create a more user friendly install package.
Programmatic IIS Administration - Selecting a Virtual Directory
This is a followup to an earlier post, Selecting a Site. The following function will return a virtual directory object, which you can then manipulate (set Directory Security options, ASP.NET version, etc.)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This sub will pass back a VirtualDirectory from a given Website Name, Virtual Directory Child 'Name
' Paramters: WebSite = Website Name
' VirDirName = Virtual Directory Name
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
function GetVirtualDirectory(WebSite,VirDirName)
dim adspath,rootobj,WebSiteObj
set WebSiteObj=GetWebSiteByName(WebSite)
set rootObj=getObject(WebSiteObj.adsPath&"/root")
adspath=rootobj.adspath
set GetVirtualDirectory=getObject(adspath&"/"&VirDirName)
end function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This sub will pass back a VirtualDirectory from a given Website Name, Virtual Directory Child 'Name
' Paramters: WebSite = Website Name
' VirDirName = Virtual Directory Name
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
function GetVirtualDirectory(WebSite,VirDirName)
dim adspath,rootobj,WebSiteObj
set WebSiteObj=GetWebSiteByName(WebSite)
set rootObj=getObject(WebSiteObj.adsPath&"/root")
adspath=rootobj.adspath
set GetVirtualDirectory=getObject(adspath&"/"&VirDirName)
end function
Monday, March 19, 2007
Assign an Application Pool using VBScript
The following script will assign a given IIS object (site or virtual directory) to an existing application pool.
Note that it relies on a helper function to check if the pool exists.
It also makes sure that the current machine is Win2003 -- you can't specify pools in prior OS's. This OS check function was detailed in a previous blog
'****************************************************************************************** ' Name: SetAppPool
' Description: Assign the IIS Object to an Application Pool (IIS 6 only)
' Inputs: ObjApp (IIS Object), strAppPool (Pool to assign to)
'******************************************************************************************
sub SetAppPool(ObjApp, strAppPool)
if instr(GetOS_Remote("."), "2K3") <> 0 then
if AppPoolExists(strAppPool) then
ObjApp.AppPoolId = strAppPool
ObjApp.SetInfo
logmsg "Set App Pool to " & strAppPool
else
logmsg "Error: App Pool " & strAppPool & " Not Found"
end if
else
logmsg "Skip app pool setting; not a 2003 server"
end if
end sub
'******************************************************************************************
' Name: AppPoolExists
' Description: See if the application pool exists (IIS 6 only)
' Inputs: strAppPool
'******************************************************************************************
function AppPoolExists(strAppPool)
dim objPool
on error resume next
Set objPool = GetObject("IIS://localhost/w3svc/AppPools/" & strAppPool)
if err.number <> 0 then
AppPoolExists = false
else
AppPoolExists = true
end if
end function
Note that it relies on a helper function to check if the pool exists.
It also makes sure that the current machine is Win2003 -- you can't specify pools in prior OS's. This OS check function was detailed in a previous blog
'****************************************************************************************** ' Name: SetAppPool
' Description: Assign the IIS Object to an Application Pool (IIS 6 only)
' Inputs: ObjApp (IIS Object), strAppPool (Pool to assign to)
'******************************************************************************************
sub SetAppPool(ObjApp, strAppPool)
if instr(GetOS_Remote("."), "2K3") <> 0 then
if AppPoolExists(strAppPool) then
ObjApp.AppPoolId = strAppPool
ObjApp.SetInfo
logmsg "Set App Pool to " & strAppPool
else
logmsg "Error: App Pool " & strAppPool & " Not Found"
end if
else
logmsg "Skip app pool setting; not a 2003 server"
end if
end sub
'******************************************************************************************
' Name: AppPoolExists
' Description: See if the application pool exists (IIS 6 only)
' Inputs: strAppPool
'******************************************************************************************
function AppPoolExists(strAppPool)
dim objPool
on error resume next
Set objPool = GetObject("IIS://localhost/w3svc/AppPools/" & strAppPool)
if err.number <> 0 then
AppPoolExists = false
else
AppPoolExists = true
end if
end function
Friday, March 16, 2007
Setting ASP.NET Version via Script
Say you have a web server running multiple versions of ASP.NET. Various applications, for better or for worse, depend on a specific version of ASP.NET (e.g. it works under 1.1; throws errors under 2.0).
Here is a function for setting the version using ADSI automation:
Code snippet moved to a dedicated blog for readability
Here is a function for setting the version using ADSI automation:
Code snippet moved to a dedicated blog for readability
CDONTS, CDOSys, and a Blank Email Address
Anyone who has migrated a legacy ASP application to Win2003/IIS6 is familiar with CDONTS and CDOSys. Code that sends email via CDONTS stops working in W2k3/IIS6 because there is no CDONTS.dll!
Code must be migrated from using CDONTS.NewMail to CDOSys.Message. Required changes are fairly straightforward.
Here's a subtle difference: your ASP application under CDONTS will not visibly fail if you provide an empty string "" for the email address. Rather, the email message will fall into your ISP's bad mail bin. CDOSys, on the other hand, will throw an error, and the application will quit on the end user if the error is unhandled.
Here are proof of concept scripts to show you the variance. You can run these from a command prompt:
CDONTS(Won't fail)
wscript.echo "creating cdonts object!"
dim mailObj : set mailObj = CreateObject ("CDONTS.NewMail")
wscript.echo "created"
mailObj.From = "TestAddress@blogger.com"
mailObj.To = ""
mailObj.Subject = "hallo"
mailObj.Body = "hello there"
mailObj.BodyFormat = 1
mailObj.MailFormat = 0
mailObj.Send()
wscript.echo "mail sent, supposedly"
set mailObj = Nothing
CDOSys(Will Fail)
dim objCDOMailer : Set objCDOMailer = CreateObject("CDO.Message")
objCDOMailer.To = ""
objCDOMailer.From = "TestAddress@blogger.com"
objCDOMailer.Subject = "hallo"
objCDOMailer.TextBody = "hello there"
objCDOMailer.Send set
objCDOMailer=nothing
wscript.echo "Message sent successfully!"
Code must be migrated from using CDONTS.NewMail to CDOSys.Message. Required changes are fairly straightforward.
Here's a subtle difference: your ASP application under CDONTS will not visibly fail if you provide an empty string "" for the email address. Rather, the email message will fall into your ISP's bad mail bin. CDOSys, on the other hand, will throw an error, and the application will quit on the end user if the error is unhandled.
Here are proof of concept scripts to show you the variance. You can run these from a command prompt:
CDONTS(Won't fail)
wscript.echo "creating cdonts object!"
dim mailObj : set mailObj = CreateObject ("CDONTS.NewMail")
wscript.echo "created"
mailObj.From = "TestAddress@blogger.com"
mailObj.To = ""
mailObj.Subject = "hallo"
mailObj.Body = "hello there"
mailObj.BodyFormat = 1
mailObj.MailFormat = 0
mailObj.Send()
wscript.echo "mail sent, supposedly"
set mailObj = Nothing
CDOSys(Will Fail)
dim objCDOMailer : Set objCDOMailer = CreateObject("CDO.Message")
objCDOMailer.To = ""
objCDOMailer.From = "TestAddress@blogger.com"
objCDOMailer.Subject = "hallo"
objCDOMailer.TextBody = "hello there"
objCDOMailer.Send set
objCDOMailer=nothing
wscript.echo "Message sent successfully!"
Thursday, March 15, 2007
Apologies for Code Snippet Formatting
Readability called, and wanted to know why she wasn't invited to this blog.
I clearly haven't mastered the posting editor. I hope to improve, or find a blog host with a better editor.
I clearly haven't mastered the posting editor. I hope to improve, or find a blog host with a better editor.
Query a Machine's OS Using WMI
Say you need to automatically configure a bunch of servers with varying operating systems. The configuration you specify will vary by OS.
For example, you can't set up an IIS Application Pool on Windows 2000, which does not run IIS 6, but you still want to install web applications and manage a similar IIS tree.
Here is a function that returns an OS indicator. You can query a remote machine's OS, provided you have administrative access. To query the local machine, use "." for the machine argument.
' Get System OS from a Remote Machine
'***************************************************************
Function GetOS_Remote(strMachine)
'WMI is required for this script to function
Dim strComputer, strWMIOS
Dim objWmiService : Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strMachine &
"\root\cimv2")
Dim strOsQuery : strOsQuery = "Select * from
Win32_OperatingSystem"
Dim colOperatingSystems : Set colOperatingSystems =
objWMIService.ExecQuery(strOsQuery)
Dim objOs
Dim strOsVer
For Each objOs in colOperatingSystems
strWmios = objOs.Caption & " " & objOs.Version
Next
Select Case True
'Add more info to the 98 and 95 to get the specific version. i.e. 98SE 95 a,b,or c
Case InStr(strWmiOS, "2000 Server") > 1 : GetOS_Remote = "2KSRV"
Case InStr(strWmiOS, "2003, Standard") > 1 : GetOS_Remote = "2K3SRV"
Case InStr(strWmiOS, "2003, Enterprise") > 1 : GetOS_Remote = "2K3ENTSRV"
Case InStr(strWmiOS, "2000 Advanced Server") > 1 : GetOS_Remote = "2KADVSRV"
Case InStr(strWmiOS, "Windows NT") > 1 : GetOS_Remote = "NT4"
Case InStr(strWmiOS, "Windows 2000") > 1 : GetOS_Remote = "W2K"
Case InStr(strWmiOS, "Windows XP") > 1 : GetOS_Remote = "WXP"
Case Else : GetOS_Remote = "Unknown"
End Select
End Function
For example, you can't set up an IIS Application Pool on Windows 2000, which does not run IIS 6, but you still want to install web applications and manage a similar IIS tree.
Here is a function that returns an OS indicator. You can query a remote machine's OS, provided you have administrative access. To query the local machine, use "." for the machine argument.
'***************************************************************
' Get System OS from a Remote Machine
'***************************************************************
Function GetOS_Remote(strMachine)
'WMI is required for this script to function
Dim strComputer, strWMIOS
Dim objWmiService : Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strMachine &
"\root\cimv2")
Dim strOsQuery : strOsQuery = "Select * from
Win32_OperatingSystem"
Dim colOperatingSystems : Set colOperatingSystems =
objWMIService.ExecQuery(strOsQuery)
Dim objOs
Dim strOsVer
For Each objOs in colOperatingSystems
strWmios = objOs.Caption & " " & objOs.Version
Next
Select Case True
'Add more info to the 98 and 95 to get the specific version. i.e. 98SE 95 a,b,or c
Case InStr(strWmiOS, "2000 Server") > 1 : GetOS_Remote = "2KSRV"
Case InStr(strWmiOS, "2003, Standard") > 1 : GetOS_Remote = "2K3SRV"
Case InStr(strWmiOS, "2003, Enterprise") > 1 : GetOS_Remote = "2K3ENTSRV"
Case InStr(strWmiOS, "2000 Advanced Server") > 1 : GetOS_Remote = "2KADVSRV"
Case InStr(strWmiOS, "Windows NT") > 1 : GetOS_Remote = "NT4"
Case InStr(strWmiOS, "Windows 2000") > 1 : GetOS_Remote = "W2K"
Case InStr(strWmiOS, "Windows XP") > 1 : GetOS_Remote = "WXP"
Case Else : GetOS_Remote = "Unknown"
End Select
End Function
Wednesday, March 14, 2007
Programmatic IIS Administration - Selecting a Site
Configuring a web server can be complicated, particularly if it is running multiple web sites, with multiple virtual directories under each site. Such a complex IIS tree requires many, many mouse clicks in order to configure according to specifications.
And what happens if the server catches on fire? It may be a good idea to script the configuration. VBScript and the IIS ADSI objects offer a multitude of possibilities.
Here is a simple VBscript function for selecting a site by name (which you can then configure with proper directory security, isolation level, host headers, etc...)
Function GetWebSiteByName(WebSiteName)
dim rootObj,count,website
set rootObj=GetObject("IIS://LocalHost/W3SVC")
for each website in rootObj
if LCase(website.class) = LCase("IIsWebServer") then
if LCase(website.ServerComment)=LCase(WebSiteName) then
set GetWebSiteByName=website
exit function
end if
end if
next
end Function
Refer back for more Automated IIS Administration examples, using low-tech script :)
And what happens if the server catches on fire? It may be a good idea to script the configuration. VBScript and the IIS ADSI objects offer a multitude of possibilities.
Here is a simple VBscript function for selecting a site by name (which you can then configure with proper directory security, isolation level, host headers, etc...)
Function GetWebSiteByName(WebSiteName)
dim rootObj,count,website
set rootObj=GetObject("IIS://LocalHost/W3SVC")
for each website in rootObj
if LCase(website.class) = LCase("IIsWebServer") then
if LCase(website.ServerComment)=LCase(WebSiteName) then
set GetWebSiteByName=website
exit function
end if
end if
next
end Function
Refer back for more Automated IIS Administration examples, using low-tech script :)
Running your Web Application under the Wrong ASP.NET Version?
- You've built an ASP.NET 2.0 app in your c:\inetpub\wwwroot directory.
- The app runs smashingly in VS.NET 2005 and when you run it from http://localhost/yourapp .
- When you upload it to your host or deploy it to your company's production server, it doesn't work!
Does this sound like a problem you've encountered? It's a familiar scenario.
Your application, built to ASP.NET 2.0, may be running under ASP.NET 1.1 or earlier. Symptoms include an inability for the ASP.NET engine to properly parse your web.config file. Or more specifically, yellow screens that include statements like:
- Parser Error Message: Unrecognized attribute 'xmlns'.
- Parser Error Message: Unrecognized configuration section "xhtmlconformance"
I would include the yellow screens, but the blog host won't upload my gifs. More later.