<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>David Wang : Tips</title><link>http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx</link><description>Tags: Tips</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>HOWTO: IIS 6 Request Processing Basics, Part 2 - Web Site, Virtual Directory, and Web Application</title><link>http://blogs.msdn.com/david.wang/archive/2008/12/07/howto-iis-6-request-processing-basics-part-2-web-site-virtual-directory-and-web-application.aspx</link><pubDate>Sun, 07 Dec 2008 19:24:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9181992</guid><dc:creator>David.Wang</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/9181992.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=9181992</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=9181992</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: courier new"&gt;
&lt;P&gt;In brief, what is different about a virtual directory that is also set as a Web Application? I can have a virtual directory, and then optionally set it to be a Web Application. Beyond updating the metabase, what does IIS do that causes the virtual directory to "be" a Web application? And how is the runtime behavior or capabilities different between a virtual directory that is not also a Web application, and one that is a Web application?&lt;/P&gt;
&lt;P&gt;Is there anything different (in terms of runtime behavior or capabilities) between a Web Application defined on a Web Site root virtual directory, as compared to a Web Application additionally defined on a virtual directory beneath a Web Site root virtual directory (i.e., a "web application within a web application")?&lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;This is an often asked question and point of confusion. I will clarify the terms from an IIS perspective.&lt;/P&gt;
&lt;H4&gt;What's in a Name?&lt;/H4&gt;
&lt;P&gt;The generic terms "web application", "virtual directory", "virtual server", and "web site" are inconsistently defined between servers/applications/platforms, so you have to understand the term's meaning in each server/application/platform and translate appropriately. In fact, even Microsoft products do not standardize on a common meaning for those terms, and due to historical legacy of each product, they will likely never change, converge, nor standardize. Sigh.&lt;/P&gt;
&lt;P&gt;For example, a Sharepoint "Virtual Server" is the same as an IIS "Web Site" and not to be confused with Microsoft's "Virtual Server" virtualization platform, which hosts virtual machines - who themselves can end up hosting Sharepoint Virtual Servers aka IIS Web Sites. Confused yet? Good. :-)&lt;/P&gt;
&lt;P&gt;IIS's terminology does not include the term "Virtual Server". When most people talk about "Virtual Server" they are often thinking of an IIS Web Site, or something that answers HTTP requests to host their logical website, which consists of a single application codebase.&lt;/P&gt;
&lt;H4&gt;Web Site&lt;/H4&gt;
&lt;P&gt;An &lt;STRONG&gt;IIS Web Site&lt;/STRONG&gt; is a mapping between a &amp;lt;IP:Port:Hostname&amp;gt; Binding triplet and a "root" Web Application (defined shortly) that responds to "/". The Web Site is how IIS figures out whether it should handle any given HTTP request and if so, with what configuration. Since this determination directly affects how a HTTP request is handled, all Binding definitions MUST be unique on a IIS machine. You do not want two Web Sites potentially fighting over the same request, right? Now, the Binding triplet is different than the "Friendly Name", which is an optional string meant for User's identification benefit. It can be "Default Web Site" or anything else, and since it is optional and not used for request handling determination, it can be duplicate or not defined.&lt;/P&gt;
&lt;P&gt;For example, suppose you have the following Web Sites with the following Binding triplets. This is what each means: 
&lt;OL&gt;
&lt;LI&gt;:80: - across all IPs of all NICs, handle port 80 traffic, regardless of Host header&lt;/LI&gt;
&lt;LI&gt;12.34.56.78:443 - only requests to IP 12.34.56.67 on port 443&lt;/LI&gt;
&lt;LI&gt;:80:Domain2.com - across all IPs of all NICs, handle port 80 traffic for requests with Host header of Domain2.com&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;With this configuration, when IIS receives any request, it knows from TCP/IP which IP:Port the request is meant for, and if the data is unencrypted, it can decipher the Host: header, and with these three pieces of information, it can determine if it matches any Web Site's Binding definition (or none) and route/handle accordingly. If it matches nothing, a "400 Bad Request" response&amp;nbsp;is returned.&lt;/P&gt;
&lt;H4&gt;SSL Host Header (sidetrack)&lt;/H4&gt;
&lt;P&gt;At this point, I will briefly digress on another topic, SSL Host Headers.&lt;/P&gt;
&lt;P&gt;Technically, there is no such thing as SSL Host Header. From the perspective of the SSL Specification, host headers do not exist because they are defined in the HTTP specification and not TCP where SSL operates.&lt;/P&gt;
&lt;P&gt;When IIS receives any request, it only knows the IP:Port&amp;nbsp;that request is destined for. In order to determine the Host header of a request, IIS must decipher the request's payload data. And to do that for an SSL request, IIS has to first decrypt the payload data by using a Server Certificate to complete the SSL handshake with the Client. However, IIS needs to know the Host header in order to know which Binding, and hence which Server Certificate, to use to decrypt the payload data and decipher the Host header. This is clearly a Catch-22.&lt;/P&gt;
&lt;P&gt;So, how does IIS implement "SSL Host Headers"? It breaks the Catch-22 by requiring all sites using SSL Host Headers for a given Binding must be configured to&amp;nbsp;use the same Server Certificate. That way, when IIS gets a IP:Port of a request, it can unambiguously use that now-synchronized Server Certificate to first decrypt the Host: header, and THEN decide which Web Site matches the IP:Port:Host Binding and route the request to it.&lt;/P&gt;
&lt;H4&gt;Web Application&lt;/H4&gt;
&lt;P&gt;A &lt;STRONG&gt;Web Application&lt;/STRONG&gt; is a mapping between a name in the virtual namespace (i.e. the URLs "/", "/App", or "/cgi-bin") and its runtime properties. These runtime properties tell IIS how to execute a request which belongs in the virtual namespace. Common runtime properties include: 
&lt;UL&gt;
&lt;LI&gt;Whether user's application code is to be executed "In Process", "Out of Process", or "Pooled" [for IIS 4/5/5.1/6 in IIS5 Compatibility Mode] or specific Application Pood ID [for IIS6 in Worker Process Isolation Mode and IIS7]&lt;/LI&gt;
&lt;LI&gt;Process Identity used to execute program code&lt;/LI&gt;
&lt;LI&gt;Monitoring/Recycling Metrics&lt;/LI&gt;
&lt;LI&gt;etc...&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;By default, whenever you create a Web Site and define the Binding (and optionally the Friendly Name), IIS also creates a "root" Web Application for "/" and asks you for a Virtual Directory mapping (defined shortly). This is because people commonly create a Web Site to host a Web Application which consists of files located at same physical directory, so defining all three features make sense... but the three concepts are definitely different.&lt;/P&gt;
&lt;H4&gt;Virtual Directory&lt;/H4&gt;
&lt;P&gt;A &lt;STRONG&gt;Virtual Directory&lt;/STRONG&gt; is a mapping between a name in the virtual namespace (i.e. the URLs "/", "/App", or "/cgi-bin") and a corresponding physical name (i.e. the Filesystem name "C:\inetpub\wwwroot\App"). It allows IIS to calculate a physical resource name for any given virtual name and provide it to the handler of the request.&lt;/P&gt;
&lt;P&gt;For example, suppose "/" maps to the physical name "C:\inetpub\wwwroot".&amp;nbsp;A request for "/default.asp" refers to the physical name "C:\inetpub\wwwroot\default.asp".&lt;/P&gt;
&lt;P&gt;The astute reader should realize that the mapping provided by a Virtual Directory is merely a "recommendation" by IIS to the request's handler - the actual handler of a request can do whatever mapping it wants with the virtual and physical names provided.&lt;/P&gt;
&lt;P&gt;In the case of /default.asp, IIS first goes through &lt;A href="http://blogs.msdn.com/david.wang/archive/2005/10/14/HOWTO_IIS_6_Request_Processing_Basics_Part_1.aspx" mce_href="http://blogs.msdn.com/david.wang/archive/2005/10/14/HOWTO_IIS_6_Request_Processing_Basics_Part_1.aspx"&gt;this process&lt;/A&gt; to figure out the handler. Suppose it ends up being ASP.DLL - it will honor the physical name C:\inetpub\wwwroot\default.asp and execute the script contained within it to generate a response.&lt;/P&gt;
&lt;P&gt;However, the handler or its script code can choose to implement its own name mapping scheme to process a given request. For example, some people write ASP pages like "redir.asp" which return different responses based on template&amp;nbsp;HTML stored within a SQL&amp;nbsp;database&amp;nbsp;depending on the querystring. i.e. /redir.asp?id=1 will load up some template HTML in SQL and generate a HTML response. Clearly, Virtual Directory is only a hint/recommendation provided by IIS to the request handler, which can do whatever it wants with the information.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;Given the above information, the answers to your questions are straight forward.&lt;/P&gt;
&lt;P&gt;A plain Virtual Directory provides a virtual/physical name mapping and MUST inherit and use the runtime settings defined at its nearest parent to execute code contained within it.&lt;/P&gt;
&lt;P&gt;A Virtual Directory that is also a Web Application has the option to inherit from its nearest parent AND customize runtime settings to execute code contained within it.&lt;/P&gt;
&lt;P&gt;Clearly, if you do not customize runtime settings, then it is not necessary to create a Web Application. And if you create a Web Application and customize runtime settings, then behavior of code execution may be different than a plain Virtual Directory (assuming that the inherited settings by the Virtual Directory&amp;nbsp;do not match the customized settings of the Web Application).&lt;/P&gt;
&lt;P&gt;As for differences between a "root" Web Application and a nested Web Application within another Web Application or Virtual Directory. IIS does not treat them&amp;nbsp;differently since Web Applications are just runtime settings. However, application platforms running on top of IIS may choose to interpret the "application root" of an Web Application differently and behave accordingly. For example, ASP.Net uses "Web Application" to delimit the boundaries of its applications, so if you nest a Web Application within another, you end up with two different ASP.Net Web Applications.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9181992" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: PreCondition an ISAPI Extension DLL</title><link>http://blogs.msdn.com/david.wang/archive/2008/10/30/howto-precondition-an-isapi-extension-dll.aspx</link><pubDate>Fri, 31 Oct 2008 08:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9025890</guid><dc:creator>David.Wang</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/9025890.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=9025890</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=9025890</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: Courier New"&gt;
&lt;P&gt;Hello&lt;/P&gt;
&lt;P&gt;We have a legacy x64 ISAPI extension that runs without problems in Win 2003 64bit - IIS 6.0.&lt;/P&gt;
&lt;P&gt;But in Win2008 EBS Security Manager setup, IIS 7.0 Default Application Pool's WOW64 / Enable32BitApp property is enabled by default, and this makes x64 ISAPI extension DLL to fail loading.&lt;/P&gt;
&lt;P&gt;By setting the Enable32BitApp option as false in application pool settings, the problem gets resolved.&lt;/P&gt;
&lt;P&gt;However we want to speicify ISAPI extension explicitly to be loaded as x64 module using "precondition='bitness64' " settings like ISAPI filters, so that ISAPI extension works no matter what application pool is configured for (x64 only, or both x64 and x86)&lt;/P&gt;
&lt;P&gt;Could anyone please suggest how we can do this or the app cmd that would allow us to do it.&lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Technically, preConditions only apply on a per-GlobalModule, per-Module, per-Handler, or per-ISAPI Filter basis. This is because globalModules, modules, handlers, and ISAPI Filters have to be explicitly configured within IIS to function. ISAPI Extensions simply need to be dropped into a web-accessible folder and then "allowed to execute" via Executable execution permission and IsapiCgiRestriction. There is no per-ISAPI Extension configuration to stick preConditions.&lt;/P&gt;
&lt;P&gt;However, as I will illustrate shortly, it is STILL possible to&amp;nbsp;apply preConditions on a per-ISAPI Extension basis in a fairly clean and clear manner.&lt;/P&gt;
&lt;P&gt;Some people would propose that it would be nice to stick&amp;nbsp;preCondition onto IsapiCgiRestriction since it is a sort of per-ISAPI Extension configuration, but IsapiCgiRestriction is really a security configuration which we hardly want to preCondition. Besides, what happens if notListedIsapisAllowed="true" and we still want to preCondition an ISAPI Extension - now we have to add a bogus entry for preCondition? Anyways, this is getting complicated very quickly, a sure sign that the proposed design has some fundamental logical flaw.&lt;/P&gt;
&lt;H4&gt;Common Misconceptions about Bitness and preConditions&amp;nbsp;&lt;/H4&gt;
&lt;P&gt;Now, before I get into how to setup per-ISAPI Extension preCondition, I want to clarify some of the misconceptions in your question about Bitness and preCondition. Using preCondition="bitness64" does NOT:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Explicitly load an ISAPI Extension as x64 module&lt;/LI&gt;
&lt;LI&gt;Make an ISAPI work no matter what the application pool is configured for&lt;/LI&gt;
&lt;LI&gt;Allow "both x64 and x86" of ANYTHING&lt;/LI&gt;&lt;/OL&gt;
&lt;P mce_keep="true"&gt;A preCondition is a simple filtering mechanism on the affected IIS configuration. What preCondition="bitness64" means is that the affected configuration is ONLY applicable in a 64bit Application Pool.&amp;nbsp;A 64bit Application Pool will only see globalModule, module, handler, and isapiFilter with either bitness64 or no bitness preCondition. It will not see any globalModule, module, handler, and isapiFilter configuration with the bitness32 preCondition.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Thus, a preCondition does NOT cause an ISAPI to be explicitly loaded as any bitness. LoadLibrary() will just load the resolved DLL name into the process's address space, and if the bitness do not match, fail. Likewise, it cannot make the ISAPI work no matter what, because if the bitness does not match, it fails. Finally, Windows does not allow both 32bit and 64bit code to co-exist in the same process, and IIS does not allow an Application Pool to be both 32bit and 64bit.&lt;/P&gt;
&lt;P mce_keep="true"&gt;What is a correct way to use the bitness preConditions? Check out the various pre-defined "-64" handlers when ASP.Net Feature support is installed. For example, the svc-ISAPI-2.0 and svc-ISAPI-2.0-64 handlers. Notice that the bitness32 preCondition applies to a 32bit ISAPI, and bitness64 preCondition applies to a separate 64bit ISAPI. This dual configuration allows the same feature to work with both 32bit and 64bit Application Pools with no additional configurations changes in-between. Remember how you had to run aspnet_regiis.exe from the correct Framework bitness directory every time you changed Application Pool bitness with .Net Framework 2.0 on IIS6 in 64bit Windows, or else you would see 503 Service Unavailable failure? No longer necessary with preConditions on IIS7 and things just work!&lt;/P&gt;
&lt;H4&gt;PreConditioning an ISAPI Extension DLL&lt;/H4&gt;
&lt;P&gt;As I mentioned earlier, per-ISAPI Extension preCondition can be accomplished by leveraging preCondition support of handlers. The following configuration shows how to request an ISAPI Extension name "MyISAPI.dll" and have it work no matter the bitness of the Application Pool. It requires a 32bit version and 64bit version of the same DLL, named MyISAPI32.dll and MyISAPI64.dll, respectively.&lt;/P&gt;&lt;PRE&gt;&amp;lt;handlers&amp;gt;
  ...
  &amp;lt;add name="MyISAPI-64" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI64.dll" resourceType="File" requireAccess="Execute" preCondition="bitness64" /&amp;gt;
  &amp;lt;add name="MyISAPI-32" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI32.dll" resourceType="File" requireAccess="Execute" preCondition="bitness32" /&amp;gt;
  ...
&amp;lt;/handlers&amp;gt;&lt;/PRE&gt;
&lt;P&gt;Looking at the key configuration details:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Having two handlers, one with bitness32 preCondition on the 32bit MyISAPI32.dll, the other with bitness64 preCondition on the 64bit MyISAPI64.dll, assures that only one of these handlers with the correct bitness apply to any given Application Pool&lt;/LI&gt;
&lt;LI&gt;path="MyISAPI.dll" means that end consumers only request /MyISAPI.dll while the dynamic 32/64bit wiring happens underneath the covers via the bitness preCondition&lt;/LI&gt;
&lt;LI&gt;requireAccess="Execute" makes Execute permissions necessary to execute an ISAPI Extension DLL in a given URL namespace&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;The combination of preConditions, path-remapping, and requireAccess gives the illusion of a&amp;nbsp;single named ISAPI DLL which transparently works with similar requirements as ISAPI Extension&amp;nbsp;regardless of Application Pool bitness.&amp;nbsp;Yes, don't forget to set IsapiCgiRestriction on both MyISAPI64.dll and MyISAPI32.dll as well as enable "Execute" permission on your virtual directory... :-)&lt;/P&gt;
&lt;P mce_keep="true"&gt;Cheers.&lt;/P&gt;
&lt;P mce_keep="true"&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9025890" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS7/default.aspx">IIS7</category></item><item><title>HOWTO: List all Virtual Directories and Paths of a List of Servers</title><link>http://blogs.msdn.com/david.wang/archive/2007/12/19/howto-list-all-virtual-directories-and-paths-of-a-list-of-servers.aspx</link><pubDate>Thu, 20 Dec 2007 08:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6813791</guid><dc:creator>David.Wang</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/6813791.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=6813791</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=6813791</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: Courier New"&gt;
&lt;P&gt;I need to audit web servers in my domain, and would like to be able to connect to each server, and enumerate the virtual directories -- ultimately leading to a link to each web site hosted by the server. Can &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/06/08/HOWTO-Enumerate-IIS-website-and-ftpsite-configuration-VBScript-using-ADSI.aspx" mce_href="http://blogs.msdn.com/david.wang/archive/2006/06/08/HOWTO-Enumerate-IIS-website-and-ftpsite-configuration-VBScript-using-ADSI.aspx"&gt;this code&lt;/A&gt; be modified to get that information?&lt;/P&gt;
&lt;P&gt;Thanks.&lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Yes, you can modify that code to get this information, but if you just want a list of virtual directories on a server, you don't need to write any script code to do it. At the end of this blog entry is one way, using a simple batch file,&amp;nbsp;to get this information using ADSUTIL.VBS, a built-in script. Just make sure to provide the right filepath for CMD_ADSUTIL. And of course, the user running the script must have administrator privileges to enumerate the IIS metabase on all required servers.&lt;/P&gt;
&lt;P&gt;This batch file accepts one optional input parameter.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;If you provide no parameter, it will enumerate all vdirs and their respective physical paths of the local computer 
&lt;LI&gt;If you provide a computer name, it will enumerate all vdirs of that computer 
&lt;LI&gt;If you provide a filepath, it will treat each line of the file as a computer name and enumerate all its vdirs &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Since I often see this feature requested, I decided to show one simple way to turn a script which takes a server name as input into one that loops through a list of server names stored in a text file, one server name on each line. This should hopefully be illustrative enough of the powerful combination of both VBScript/JScript and Batch script.&lt;/P&gt;
&lt;H4&gt;Sample usage:&lt;/H4&gt;&lt;PRE&gt;C:\&amp;gt;enumvdirs -?
enumvdirs [servername | file-list]

Where:
    servername is the name of the server to query. DAVIDWANG by default
    file-list is filepath to text file containing list of servers, one per line

C:\&amp;gt;enumvdirs DAVIDWANG
DAVIDWANG/W3SVC/1/ROOT = "c:\inetpub\wwwroot"
DAVIDWANG/W3SVC/1/ROOT/IISHelp = "c:\windows\help\iishelp"
DAVIDWANG/W3SVC/1/ROOT/Printers = "C:\WINDOWS\web\printers"
DAVIDWANG/W3SVC/1/ROOT/Scripts = "C:\Inetpub\Scripts"

C:\&amp;gt;ECHO %COMPUTERNAME% &amp;gt; ListOfServers.txt

C:\&amp;gt;TYPE ListOfServers.txt
DAVIDWANG

C:\&amp;gt;enumvdirs ListOfServers.txt
DAVIDWANG/W3SVC/1/ROOT = "c:\inetpub\wwwroot"
DAVIDWANG/W3SVC/1/ROOT/IISHelp = "c:\windows\help\iishelp"
DAVIDWANG/W3SVC/1/ROOT/Printers = "C:\WINDOWS\web\printers"
DAVIDWANG/W3SVC/1/ROOT/Scripts = "C:\Inetpub\Scripts"&lt;/PRE&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: Courier New"&gt;&lt;PRE&gt;@IF NOT DEFINED _ECHO ECHO OFF
SETLOCAL
SET CMD_ADSUTIL=CSCRIPT.EXE //Nologo %SYSTEMDRIVE%\Inetpub\Adminscripts\ADSUTIL.VBS
SET PROPERTY_TO_FIND=Path

SET SERVERS="%1"
IF ?%1? EQU ?? SET SERVERS="%COMPUTERNAME%"
IF EXIST %SERVERS% SET SERVERS=%SERVERS:~1,-1%

SET NEED_HELP=%SERVERS:?=%
IF /I "%NEED_HELP%" NEQ "%SERVERS%" GOTO :Help

FOR /F %%A IN ( %SERVERS% ) DO (
    FOR /F "usebackq skip=1 tokens=*" %%I IN ( `%CMD_ADSUTIL% FIND %PROPERTY_TO_FIND% -s:%%A` ) DO (
        FOR /F "usebackq tokens=3,*" %%J IN ( `%CMD_ADSUTIL% GET %%I/%PROPERTY_TO_FIND% -s:%%A` ) DO (
            ECHO %%A/%%I = %%K
        )
    )
)

ENDLOCAL
GOTO :EOF



:Help
ECHO %0 [servername ^| file-list]
ECHO.
ECHO Where:
ECHO     servername is the name of the server to query. %COMPUTERNAME% by default
ECHO     file-list is filepath to text file containing list of servers, one per line

GOTO :EOF&lt;/PRE&gt;&lt;/SPAN&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6813791" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Basic Native/Managed Code Interop</title><link>http://blogs.msdn.com/david.wang/archive/2006/07/13/HOWTO-Basic-Native-Managed-Code-Interop.aspx</link><pubDate>Thu, 13 Jul 2006 14:59:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:663194</guid><dc:creator>Anonymous</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/663194.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=663194</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=663194</wfw:comment><description>&lt;P&gt;IIS7 core extensibility model supports both native and managed code as first-class citizens. So, I feel it is time for a little refresher on managed/native code interop... starting with the more popular route of how to wrap native code API for use within managed code. I am using the newer syntax introduced with .Net Framework 2.0 instead of the older, kludgy syntax.&lt;/P&gt;
&lt;P&gt;Now, I am going to ignore&amp;nbsp;the reciprocal&amp;nbsp;route of calling managed code from native code for a couple of reasons:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;It is just boiler plate COM Interop within native code after generating and registering the CCW (COM Callable Wrapper) of the managed class. 
&lt;LI&gt;Why don't you just write a managed code module/handler in IIS7 to directly use that managed API?&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The example illustrates how to use Managed Code to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Pass a .NET String into Managed C++ 
&lt;LI&gt;Manipulate a .NET String in Managed C++ 
&lt;LI&gt;Return a .NET String from Managed C++ 
&lt;LI&gt;Pass in arbitrary number of args into Managed C++ &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Remember to use a Class Library Project for Sample.h and Sample.cpp to create a Managed C++ Wrapper around native code API, and you can use the resulting Managed Assembly from Sample.cs managed code.&lt;/P&gt;
&lt;P&gt;Enjoy,&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;
&lt;H4&gt;Sample.h&lt;/H4&gt;&lt;FONT color=#00800&gt;&lt;PRE&gt;#pragma once
#include &amp;lt;windows.h&amp;gt;
#include "SomeNativeAPI.h"

using namespace System;
using namespace System::Runtime::InteropServices;

namespace Sample
{
    public ref class ManagedClass
    {
    public:
        ManagedClass( String^ name );
        ~ManagedClass();
        !ManagedClass();
        String^ DebugPrint( String^ format, ...array&amp;lt;String^&amp;gt;&lt;STRING^&gt;^ args );
    private:
        SomeNativeType* m_pType;
    };
}&lt;/PRE&gt;&lt;/FONT&gt;
&lt;H4&gt;Sample.cpp&lt;/H4&gt;&lt;FONT color=#00800&gt;&lt;PRE&gt;#include "Sample.h"

Sample::ManagedClass::ManagedClass( String^ name )
{
    //
    // Convert .NET String into LPSTR for 
    // Native code API to use in constructor
    //
    IntPtr szName;
    szName = Marshal::StringToHGlobalAnsi( name );
    m_pType = new SomeNativeType( szName );
    Marshal::FreeHGlobal( szName );
}
Sample::ManagedClass::~ManagedClass()
{
    this-&amp;gt;!ManagedClass();
}
Sample::ManagedClass::!ManagedClass()
{
    delete m_pType;
}
String^ Sample::ManagedClass::DebugPrint( String^ format, ...array&amp;lt;String^&amp;gt;&lt;STRING^&gt;^ args )
{
    //
    // Use Managed Code to format variable arguments as .NET String,
    // convert the .NET String into Unicode String, and pass
    // it to Native API
    //
    String^ formattedString = System::String::Format( format, args );
    IntPtr  wszFormattedString;

    wszFormattedString = Marshal::StringToHGlobalUni( formattedString );
    m_pType-&amp;gt;SomeFunctionUnicode( wszFormattedString );
    Marshal::FreeHGlobal( wszFormattedString );

    return formattedString;
}&lt;/PRE&gt;&lt;/FONT&gt;
&lt;H4&gt;Sample.cs&lt;/H4&gt;&lt;FONT color=#00800&gt;&lt;PRE&gt;namespace Sample
{
    class Program
    {
        static void Main( string[] args )
        {
            Sample.ManagedClass cls = new Sample.ManagedClass( "Name?" );
            System.Console.WriteLine( cls.DebugPrint( "0:{0},1:{1}", "N", "V" ) );
        }
    }
}&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=663194" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Convert between JScript Array and VB Safe Array</title><link>http://blogs.msdn.com/david.wang/archive/2006/07/04/HOWTO-Convert-between-JScript-Array-and-VB-Safe-Array.aspx</link><pubDate>Tue, 04 Jul 2006 12:10:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:655884</guid><dc:creator>Anonymous</dc:creator><slash:comments>11</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/655884.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=655884</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=655884</wfw:comment><description>&lt;P&gt;I recently got a question about how to manipulate the LIST data type within JScript since my &lt;A href="http://blogs.msdn.com/david.wang/archive/2004/12/02/273681.aspx"&gt;sample code&lt;/A&gt; only illustrated VBScript.&lt;/P&gt;
&lt;P&gt;Well... one reason why that example is in VBScript is because LIST manipulation (a VB SafeArray) is more straight forward and requires much less code in VBScript.&lt;/P&gt;
&lt;P&gt;Then, there is Microsoft documentation which says the following time and again (and echo'd in lots of other places):&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;There is currently no way to convert a JavaScript array into a VBArray&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Really? I could not believe it. A quick search of the Internet turned up no results of how to do this, either... so it sounds like a good challenge to me. :-)&lt;/P&gt;
&lt;P&gt;I racked my brain a little, and I came up with the following built-in solution to the problem of converting a JScript array into a VB SafeArray. Yes, I know it is not efficient, but for casual conversion for the purposes of IIS configuration, it definitely suffices. And it works on Windows 2000 on up with no additional requirements, a definite bonus from a dependency/ubiquity point-of-view.&lt;/P&gt;
&lt;P&gt;See the following JScript code which manipulates the IIS ScriptMaps property (a VB SafeArray) by first converting the SafeArray into JScript array, then manipulating it in JScript, and finally converting it back to a VB SafeArray for use by the Scriptmaps LIST property.&lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;var objRoot = GetObject( "IIS://localhost/w3svc/1/root" );
var arrVB = objRoot.Get( "ScriptMaps" );

//
// Convert from VB Array for manipulation in JScript
//
var arrJS = VB2JSArray( arrVB );
//
// Insert a test Value at end of ScriptMaps
//
arrJS[ arrJS.length ] = ".test,value,0";
//
// Convert back to VB Array to save by IIS
//
objRoot.ScriptMaps = JS2VBArray( arrJS );
objRoot.SetInfo();

function VB2JSArray( objVBArray )
{
    return new VBArray( objVBArray ).toArray();
}

function JS2VBArray( objJSArray )
{
    var dictionary = new ActiveXObject( "Scripting.Dictionary" );
    for ( var i = 0; i &amp;lt; objJSArray.length; i++ )
    {
        dictionary.add( i, objJSArray[ i ] );
    }

    return dictionary.Items();
}&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=655884" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Monitor Virtual Server Events</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/29/HOWTO-Monitor-Virtual-Server-Events.aspx</link><pubDate>Thu, 29 Jun 2006 15:14:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:650686</guid><dc:creator>Anonymous</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/650686.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=650686</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=650686</wfw:comment><description>&lt;P&gt;I think Virtual Server Events and Asynchronous Tasks are two of the least utilized features of the Virtual Server Administration API.&lt;/P&gt;
&lt;P&gt;Contrary to the often-asked task of "find the VM, turn it off, manipulate its VHD, then turn it back on", which shows the synchronous, task-driven side of the VS Admin model, Events and Asynchronous Tasks show off a nice event-based, asynchronous model suitable for passive monitoring and response. What good is this?&lt;/P&gt;
&lt;P&gt;Well... that's the motivation behind this blog entry's sample code, which illustrates a simple "Virtual Server Monitor Agent" which does a few basic things:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;By default, it simply monitors all Virtual Server events and reports them to the console window. Assuming you named the JScript code VSMonitor.js, just launch it from the commandline with:&lt;BR&gt;START CSCRIPT VSMonitor.js 
&lt;LI&gt;Thereafter, whenever you start a Virtual Machine, a new child monitor script will pop up and monitor just that Virtual Machine 
&lt;LI&gt;If that Virtual Machine has a Virtual DVD drive, they will also be monitored for media insertion/ejection and type (ISO or drive letter path) 
&lt;LI&gt;When you turn off a Virtual machine, its associated child monitor script will also terminate (but it will first popup a message telling you this, in case you missed it). 
&lt;LI&gt;It also notices when you fail to startup a Virtual Machine due to Out-of-Memory and report that via a popup&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The sample basically illustrates how to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Sync on Virtual Server wide events 
&lt;LI&gt;Act on a Virtual Server wide event 
&lt;LI&gt;Sync on Virtual Machine specific events 
&lt;LI&gt;Act on&amp;nbsp;a Virtual Machine specific event 
&lt;LI&gt;Sync on Virtual DVD specific events 
&lt;LI&gt;Report on Virtual DVD events&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;And it should provide enough information for you to play around, note when events happen, and note available data for the event... and implement other interesting ideas supported by the API.&amp;nbsp;I don't have any in mind, but if you do... feel free to share... because then I may update this sample to do something more interesting. :-)&lt;/P&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;var strVMName = "sample VM Name";
var CRLF = "\r\n";
var NO_WAIT = false;
var VISIBLE = 1;

var ERROR_VM_NOT_FOUND = -2146828283;
var ERROR_VM_OUT_OF_MEMORY = 3238134821;

var fSignaled = false;
var fPopup = true;
var cPeriod = 10000;

var VM_STATE_STRING = {
    0 : "Invalid",
    1 : "Off",
    2 : "Saved",
    3 : "Turning on",
    4 : "Restoring",
    5 : "Running",
    6 : "Paused",
    7 : "Saving",
    8 : "Turning off",
    9 : "Merging drives",
    10 : "Deleting VM"
};
var VM_STATE_OFF = 1;
var VM_STATE_RUNNING = 5;

var objVS = null;
var objVM = null;
var objDVD = null;
var objShell = null;
var enumObjs;
var enumObj;

if ( WScript.Arguments.Length &gt; 0 )
{
    strVMName = WScript.Arguments.Item( 0 );
}

try
{
    objShell = new ActiveXObject( "WScript.Shell" );
    objVS = new ActiveXObject( "VirtualServer.Application" );

    try
    {
        //
        // Try to locate the specified VM and listen for its events
        // If fail to locate VM, just listen for VS-wide events
        //
        objVM = objVS.FindVirtualMachine( strVMName );
        WScript.ConnectObject( objVM, "VirtualMachineEvent_" );

        //
        // If the VM has any DVD drives, listen for their changes, too
        //
        enumObjs = new Enumerator( objVM.DVDROMDrives );

        for ( ; !enumObjs.atEnd(); enumObjs.moveNext() )
        {
            objDVD = enumObjs.item();
            WScript.ConnectObject( objDVD, "VirtualDVDEvent_" );
        }
    }
    catch ( e )
    {
        if ( e.number == ERROR_VM_NOT_FOUND )
        {
            LogEcho( "Failed to find VM " + strVMName + "." );
        }
        else
        {
            LogEcho( formatErrorString( e ) );
        }

        objVM = null;
        WScript.ConnectObject( objVS, "VirtualServerEvent_" );
    }

    LogEcho( "Waiting for Events from: " + CRLF +
             ( objVM  != null ? "- VM " + strVMName + CRLF : "- VS Wide" ) +
             ( objDVD != null ? "- DVD Drive(s)" + CRLF : "" ) +
             "" );

    //
    // Poll every cPeriod time period to determine if should exit
    //
    while ( !fSignaled )
    {
        WScript.Sleep( cPeriod );
    }
}
catch ( e )
{
    LogEcho(
        formatErrorString( e ) + CRLF +
        ( objShell == null ?
            "Failed to create WScript.Shell - check:" + CRLF +
            "- %windir%\System32\WSHOM.OCX is ACLed for user" + CRLF +
            "- Scripts are allowed by Personal Security products" + CRLF :
            "" ) +
        ( objVS == null ?
            "Failed to create Virtual Server COM object - check:" + CRLF +
            "- Virtual Server is installed" + CRLF +
            "- User has Control/Execute Access in VS Security" + CRLF :
            "" )
            );
    LogEcho( "Waiting a few seconds for you to read this..." );
    WScript.Sleep( cPeriod );
}

function LogEcho( str )
{
    WScript.Echo( str );
}

function LogPopup( str )
{
    if ( fPopup )
    {
        objShell.Popup( str );
    }
    else
    {
        LogEcho( str );
    }
}

function formatErrorString( objError )
{
    return "(" + Int32ToHRESULT( objError.number ) + ")" + ": " +
           objError.description;
}

function Int32ToHRESULT( num )
{
    if ( num &lt; 0 )
    {
        return "0x" + new Number( 0x100000000 + num ).toString( 16 );
    }
    else
    {
        return "0x" + num.toString( 16 );
    }
}

//
// Virtual Server Events
//
function VirtualServerEvent_OnEventLogged( e )
{
    LogEcho( "VirtualServer_OnEventLogged: (Event ID) " + e );

    if ( e == ERROR_VM_OUT_OF_MEMORY )
    {
        LogPopup( "Cannot start VM due to out of memory" );
    }
}
function VirtualServerEvent_OnHeartbeatStopped( e )
{
    LogEcho( "VirtualServer_OnHeartbeatStopped: (VMName) " + e );
}
function VirtualServerEvent_OnServiceEvent( e )
{
    LogEcho( "VirtualServer_OnServiceEvent: (Event ID) " + e );
}
function VirtualServerEvent_OnVMStateChange( e, s )
{
    var retVal;
    LogEcho( "VirtualServer_OnVMStateChange: VM " + e + " " +
             "changed to state " + s + "(" + VM_STATE_STRING[ s ] + ")" );
    switch ( s )
    {
        case VM_STATE_RUNNING:
            LogEcho( "Running " + WScript.ScriptFullName +
                     " to monitor VM " + e );
            retVal = objShell.Run(
                        "CSCRIPT.EXE" + " " +
                        "\"" + WScript.ScriptFullName + "\"" + " " +
                        "\"" + e + "\"",
                        VISIBLE,
                        NO_WAIT );
            break;
    }
}

function VirtualMachineEvent_OnConfigurationChanged( e, s )
{
    LogEcho( "VirtualMachine_OnConfigurationChanged: " + strVMName + " " +
             "config " + e + " = " + "\"" + s + "\"" );
}
function VirtualMachineEvent_OnHeartbeatStopped()
{
    LogEcho( "VirtualMachine_OnHeartbeatStopped: " + strVMName );
}
function VirtualMachineEvent_OnRequestShutdown()
{
    LogEcho( "VirtualMachine_OnRequestShutdown: " + strVMName );
    //
    // return true/false to reboot
    //
    return false;
}
function VirtualMachineEvent_OnReset()
{
    LogEcho( "VirtualMachine_OnReset: " + strVMName );
}
function VirtualMachineEvent_OnStateChange( e )
{
    LogEcho( "VirtualMachine_OnStateChange: " + strVMName + " " +
             "changed to state " + e + "(" + VM_STATE_STRING[ e ] + ")" );
    switch ( e )
    {
        case VM_STATE_OFF:
            LogPopup( "Signaled to stop monitoring VM " + strVMName );
            fSignaled = true;

            break;
    }
}
function VirtualMachineEvent_OnTripleFault()
{
    LogEcho( "VirtualMachine_OnTripleFault: " + strVMName );
}

function VirtualDVDEvent_OnMediaInsert( e )
{
    LogEcho( "VirtualDVDEvent_OnMediaInsert: " + strVMName + " " +
             "DVD is bound to " + e );
}
function VirtualDVDEvent_OnMediaEject( e )
{
    LogEcho( "VirtualDVDEvent_OnMediaEject: " + strVMName + " " +
             "DVD is not bound to " + e );
}&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=650686" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Virtual+Server/default.aspx">Virtual Server</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Install and Run PHP on IIS7, Part 3</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/24/HOWTO-Install-and-Run-PHP-on-IIS7-Part-3.aspx</link><pubDate>Sat, 24 Jun 2006 13:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:644001</guid><dc:creator>Anonymous</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/644001.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=644001</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=644001</wfw:comment><description>&lt;P&gt;A short while ago, I came out with a &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/06/21/HOWTO_Install_and_Run_PHP_on_IIS7_Part_2.aspx"&gt;small script&lt;/A&gt; to properly configure PHP for IIS7. Sure enough, I got asked to make a similar one for IIS6. So, I figure that while I am at it, I might as well cover&amp;nbsp;IIS4, IIS5, and IIS5.1 as well since it is not very different, and put it all into one script so that you can see one classic way to maintain a single automation script which runs version-specific logic.&lt;/P&gt;
&lt;P&gt;For the astute reader - no, this script is not minimal, optimal, nor foolproof... it is illustrative. I am deliberately showing several possibilities at the expense of conciseness... but I hope you agree that the information is worth more than the result here...&lt;/P&gt;
&lt;P&gt;To correctly configure the PHP Application Mapping prior to IIS7, you MUST provide a tool that can modify the IIS LIST data type.&lt;/P&gt;
&lt;P&gt;I have provided such a tool in &lt;A href="http://blogs.msdn.com/david.wang/archive/2004/12/02/273681.aspx"&gt;this blog entry&lt;/A&gt;, so you need to copy that script tool into the same directory as you copy this script and name it "ChgList.vbs". If you want to put the tool in a different directory or with a different name, you must modify the FILE_CHGLIST variable in this script appropriately to give the complete pathname.&lt;/P&gt;
&lt;P&gt;In addition, I made several little illustrative enhancements:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Debug mode - if you want to merely SEE what is going to execute but NOT execute anything, set the _DEBUG environment variable to 1. Default executes. 
&lt;LI&gt;Functions vs. Labels - the label :VerifyScripts is treated as a FUNCTION in batch (with ERRORLEVEL as the return value), while the labels :Menu and :Start are treated like GOTO labels 
&lt;LI&gt;File Existence Validations - depending on the OS/IIS Version, validate the existence of necessary files and scripts 
&lt;LI&gt;OS BuildNumber detection&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Yes, when you write scripts/tools meant to run on multiple platforms and versions, you get constrained into the most reliable least-common-denominator and never get to use the new-fangled stuff. But that's the difference between getting stuff done with compatible software vs experimenting with the bleeding edge... ;-)&lt;/P&gt;
&lt;P&gt;In this case, I am using OS BuildNumber, parsed from 'ver', to determine IIS version. If I can constrain this script to IIS5.1 and above (or W2K with REG.EXE from the Resource Kit), I can use REG.EXE to read the installed IIS version from the Registry... but I am not making those assumptions&amp;nbsp;and hence use the OS BuildNumber as a compatible mechanism.&lt;/P&gt;
&lt;P&gt;And to be complete - all of these actions require you to run with administrative privileges since you cannot modify the IIS Configuration file(s) without them. Prior to Vista, this means the user must be in the local Administrators group. On Vista with UAC (default), it means that you either run as the built-in Administrator (disabled by default) or run the script with elevated permissions (by saving the script and right-click running as Administrator).&lt;/P&gt;
&lt;P&gt;One final disclaimer:&lt;/P&gt;
&lt;P&gt;*** Please realize that the script tool simply makes PHP work in one configuration (default). It is not meant to fix or make your arbitrary configuration work ***&lt;/P&gt;
&lt;P&gt;In particular, if you run this script more than once, it may not work correctly or configure duplicate settings. For example, APPCMD will fail to configure duplicate handlers&amp;nbsp;and WebServiceExtension entries, iisext.vbs will fail to configure duplicate WebServiceExtension entries, and ChgList.vbs will keep adding duplicate .php ScriptMappings. It is a slippery slope, so I draw the line early.&lt;/P&gt;
&lt;P&gt;Also, the script does not go through your ScriptMaps to change the right ones; you can do that yourself. It also does not verify file ACLs, user identities and permissions, etc - it assumes everything is working perfectly and you just need to make the minimal IIS-related configuration to make PHP work.&lt;/P&gt;
&lt;P&gt;Sorry... but please understand that I am not in the business of writing and supporting installation programs for other products nor troubleshooting why it does not work on IIS. I am just trying to show how things work, together.&lt;/P&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;@IF ?%_ECHO%?==?? ECHO OFF

SETLOCAL
IF ?%_DEBUG%? EQU ?1? (SET DEBUG=ECHO) ELSE (SET DEBUG=)
SET FILE_CHGLIST=chglist.vbs
SET FILE_IISEXT=%SYSTEMROOT%\System32\iisext.vbs
SET CMD_CHGLIST=CSCRIPT //NoLogo %FILE_CHGLIST%
SET CMD_IISEXT=CSCRIPT //NoLogo %FILE_IISEXT%
SET CMD_APPCMD=%SYSTEMROOT%\System32\inetsrv\APPCMD.EXE
SET DIR_PHP_FROM=%SYSTEMDRIVE%\Inetpub\PHP
SET PHP_TYPE=ISAPI
SET PHP_MODULE=IsapiModule
SET PHP_BINARY=php5isapi.dll

REM
REM Determine OS BuildNumber
REM 1381    NT4     IIS4
REM 2195    W2K     IIS5
REM 2600    WXP     IIS5.1
REM 3790    WS03    IIS6
REM Other   Vista   IIS7
REM
FOR /f "tokens=3 delims=.]" %%i IN ('ver') DO SET OS_BUILDNUMBER=%%i
IF "%OS_BUILDNUMBER%"=="" FOR /f "tokens=4" %%i IN ('ver') DO IF "%%i"=="4.0" SET OS_BUILDNUMBER=1381

:Menu
ECHO.
ECHO David.Wang's Sample PHP/IIS Configurator
ECHO Version: June 2006
ECHO OS BuildNumber: %OS_BUILDNUMBER%
ECHO.
ECHO ------------------------------ Summary ------------------------------
ECHO PHP Binaries Dir : %DIR_PHP_FROM%
ECHO PHP Binary Type  : %PHP_TYPE%
ECHO PHP Binary Name  : %PHP_BINARY%
ECHO ---------------------------------------------------------------------

REM
REM Do some basic validations
REM
ECHO.
ECHO Validating inputs...
IF /I ?%PHP_TYPE%? NEQ ?CGI? IF /I ?%PHP_TYPE%? NEQ ?ISAPI? ECHO.&amp;amp;ECHO ERROR: Binary Type MUST be either CGI or ISAPI
FOR %%I IN ( %PHP_BINARY% ) DO (
    IF /I ?%PHP_TYPE%? EQU ?CGI? IF /I ?%%~xI? NEQ ?.exe? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires a CGI EXE binary
    IF /I ?%PHP_TYPE%? EQU ?ISAPI? IF /I ?%%~xI? NEQ ?.dll? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires an ISAPI DLL binary
)
IF /I ?%PHP_TYPE%? EQU ?CGI? SET PHP_MODULE=CgiModule
IF /I ?%PHP_TYPE%? EQU ?CGI? ECHO.&amp;amp;ECHO ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "%DIR_PHP_FROM%\PHP.INI"
IF /I ?%PHP_BINARY%? NEQ ?php5isapi.dll? IF /I ?%PHP_BINARY%? NEQ ?php-cgi.exe? ECHO.&amp;amp;ECHO WARNING: Unrecognized PHP binary %PHP_BINARY%
Call :VerifyScripts
ECHO.
ECHO Remember to tweak PHP.INI for security and functionality per php.net
ECHO Finished input validation.
ECHO.

SET GO=
SET /P GO=Press 1 to EDIT choices, or ENTER to start IIS modifications:
IF ?%GO%? EQU ?? GOTO :Start

ECHO.
ECHO Press ENTER to accept [%DIR_PHP_FROM%], or provide new value (folder path)
SET /P DIR_PHP_FROM=PHP Binaries Dir:
ECHO Press ENTER to accept [%PHP_TYPE%], or provide new value (CGI or ISAPI)
SET /P PHP_TYPE=PHP Binary Type:
ECHO Press ENTER to accept [%PHP_BINARY%], or provide new value (filename)
SET /P PHP_BINARY=PHP Binary Name:

GOTO :Menu

:Start
REM
REM Start Configuration
REM
ECHO.
ECHO Starting IIS Configuration...
ECHO.
ECHO Copying "%DIR_PHP_FROM%\PHP.INI-Recommended" to PHP.INI...
%DEBUG% COPY /Y "%DIR_PHP_FROM%\PHP.INI-Recommended" "%DIR_PHP_FROM%\PHP.INI"

CALL :VerifyScripts
IF %ERRORLEVEL% EQU 2 GOTO :EOF

REM
REM Use OS Version to distinguish between IIS Versions
REM
REM 1381    NT4     IIS4
REM 2195    W2K     IIS5
REM 2600    WXP     IIS5.1
REM 3790    WS03    IIS6
REM Other   Vista   IIS7
REM
IF %OS_BUILDNUMBER% GTR 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_APPCMD% SET CONFIG -section:handlers "-+[name='PHP-%PHP_TYPE%',path='*.php',verb='GET,HEAD,POST',modules='%PHP_MODULE%',scriptProcessor='%DIR_PHP_FROM%\%PHP_BINARY%',resourceType='File']"

    ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
    %DEBUG% %CMD_APPCMD% SET CONFIG -section:isapiCgiRestriction "-+[path='%DIR_PHP_FROM%\%PHP_BINARY%',allowed='true',groupId='PHP',description='PHP']"
) ELSE IF %OS_BUILDNUMBER% EQU 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_CHGLIST% W3SVC/ScriptMaps "" ".php,%DIR_PHP_FROM%\%PHP_BINARY%,0" /INSERT /COMMIT
    ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
    %DEBUG% %CMD_IISEXT% /AddFile "%DIR_PHP_FROM%\%PHP_BINARY%" 1 PHP 1 PHP
) ELSE IF %OS_BUILDNUMBER% LSS 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_CHGLIST% W3SVC/ScriptMaps "" ".php,%DIR_PHP_FROM%\%PHP_BINARY%,0" /INSERT /COMMIT
)

ECHO.
ECHO Finished IIS Configuration.
ECHO.
ECHO Test installation using PHP file content of:  ^&amp;lt;?php phpinfo();?^&amp;gt;

ENDLOCAL
GOTO :EOF

REM
REM Sub-routines and Functions
REM
:VerifyScripts
SET ERRORLEVEL=0
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
    SET ERRORLEVEL=2
)
IF NOT EXIST "%DIR_PHP_FROM%\PHP.INI" (
    ECHO.
    ECHO ERROR: "%DIR_PHP_FROM%\PHP.INI" does not exist!
    SET ERRORLEVEL=2
)
IF %OS_BUILDNUMBER% GTR 3790 (
    IF NOT EXIST "%CMD_APPCMD%" (
        ECHO.
        ECHO ERROR: Script requires %CMD_APPCMD% for this OS.
        SET ERRORLEVEL=2
    )
)
IF %OS_BUILDNUMBER% EQU 3790 (
    IF NOT EXIST "%FILE_IISEXT%" (
        ECHO.
        ECHO ERROR: Script requires %FILE_IISEXT% for this OS.
        SET ERRORLEVEL=2
    )
)
IF %OS_BUILDNUMBER% LEQ 3790 (
    IF NOT EXIST "%FILE_CHGLIST%" (
        ECHO.
        ECHO ERROR: Script requires %FILE_CHGLIST% for this OS.
        ECHO http://blogs.msdn.com/david.wang/archive/2004/12/02/273681.aspx
        SET ERRORLEVEL=2
    )
)
GOTO :EOF&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=644001" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>Anatomy of an Application Pool Crash</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/22/Anatomy-of-an-Application-Pool-Crash.aspx</link><pubDate>Thu, 22 Jun 2006 15:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:642777</guid><dc:creator>Anonymous</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/642777.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=642777</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=642777</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Ok, I read through David Wang's &lt;A href="http://blogs.msdn.com/david.wang/archive/2005/08/29/HOWTO_Understand_and_Diagnose_an_AppPool_Crash.aspx"&gt;Troubleshooting crashes&lt;/A&gt; thing and got the DebugDiag and I am able to reproduce the problem.&lt;/P&gt;
&lt;P&gt;If I select Hang, and type in one of the website addresses that we host on this server, the moment the Hang Test starts it brings the entire Application Pool to a stand still. All other sites in other application pools respond perfectly, just not in this application pool.&lt;/P&gt;
&lt;P&gt;The moment I terminate the w3wp.exe process for this application pool, IIS respawns a new process and all of a sudden all the sites that were hanging respond instantly.&lt;/P&gt;
&lt;P&gt;Any idea of what may be causing this?&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Actually, what you describe as "causing this" is by-design. Things are working exactly the way it should when you catch a crash with a debugger like DebugDiag. Hmm... this reminds me that I should hurry up and complete that blog entry describing native code debugging of IIS6... but let's get back to the question at hand.&lt;/P&gt;
&lt;H4&gt;Handling the Unhandled Exception&lt;/H4&gt;
&lt;P&gt;What is happening is that when you select Hang, DebugDiag attaches a debugger onto the w3wp.exe process and waits for an unhandled exception to happen. An unhandled exception is an exception that is not expected, usually caused by human logical error (i.e. "bug"), and it triggers an immediate execution halt of the process. The raising of the exception basically indicates a crash is ABOUT to happen, which is why a debugger waits on it. You want to debug the process and its state right as the exception happens so that you can figure out the human logical error and fix it.&lt;/P&gt;
&lt;P&gt;So, as soon as the unhandled exception happens, the attached debugger seizes control of the process, and NOTHING runs in the process because the debugger preserves the failing state of the process for investigation.&lt;/P&gt;
&lt;H4&gt;Debugging Application Pools&lt;/H4&gt;
&lt;P&gt;Depending on the configuration of the Application Pool, when&amp;nbsp;a debugger seizes control of its w3wp.exe, ALL sites/applications using this Application Pool may simply grind to a standstill and not run.&lt;/P&gt;
&lt;P&gt;However, options exist for having the Application Pool function while a worker process is debugged, and they include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Web Garden - other worker processes take up the slack while this worker process gets debugged. 
&lt;LI&gt;Orphaning - WAS will orphan and "forget" about this w3wp.exe, so new w3wp.exe will be spawned to handle future requests&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Of course, these options come with their own unique set of caveats. You will have to evaluate them to determine if your situation benefits. They are not defaults for good reasons.&lt;/P&gt;
&lt;P&gt;As soon as you terminate the w3wp.exe (or exit the debugger), the attached debugger also terminates. At this point, WAS detects this as an unexpected crash because WAS waits on all worker processes' handles, and this one just went away unexpectedly (i.e. not due to a triggered process recycle), so it logs it in the Event Log.&lt;/P&gt;
&lt;P&gt;Then, depending on health-monitoring metrics (i.e. have not unexpectedly crashed too many times recently), WAS will keep the Application Pool active for HTTP.SYS, meaning that requests continue to queue and spawn new w3wp.exe process as appropriate to handle them.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;This is why as soon as you terminate the w3wp.exe, the other sites become responsive. The requests were all queued and placed on-hold because a debugger halted the w3wp.exe that will handle them and the Application Pool is not configured to allow another w3wp.exe to handle them. As soon as the old w3wp.exe and associated debugger are out of the picture, a new w3wp.exe gets spawned to immediately handle the queued requests.&lt;/P&gt;
&lt;P&gt;Thus, the "hang" you observe only happens when a debugger is attached to the crashing worker process and the Application Pool is configured in particular ways, and that is by-design. In the "normal" case where a debugger is not attached, the unhandled exception simply bubbles up to either the configured JIT Debugger or Windows itself, who usually just identify it as a crash and immediately terminates the process... and the subsequent requests simply spawn up a new w3wp.exe and continue onwards. You never see a "hang".&lt;/P&gt;
&lt;P&gt;So, the IIS6 sequence is pretty darn optimal when it comes to handling code crashing at runtime and then gracefully recovering. It just may not appear that way at first glance.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=642777" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category></item><item><title>HOWTO: Install and Run PHP on IIS7, Part 2</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/21/HOWTO-Install-and-Run-PHP-on-IIS7-Part-2.aspx</link><pubDate>Thu, 22 Jun 2006 06:39:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:642354</guid><dc:creator>Anonymous</dc:creator><slash:comments>50</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/642354.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=642354</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=642354</wfw:comment><description>&lt;P&gt;A couple of months ago, I wrote a quick and dirty &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/04/04/HOWTO_Install_and_run_PHP_on_IIS7.aspx"&gt;entry&lt;/A&gt; on how to install PHP on IIS7. The main purpose of that entry was to explain the details of what was going on as well as cookie-cutter instructions of one way to successfully install PHP on IIS7.&lt;/P&gt;
&lt;P&gt;Well, the responses that I have received from that blog entry made me realize that I need to provide something a little more shrink-wapped which does a few more things other than just run the bare minimum... because I was seeing way too many broken custom modifications coming from mistaken assumptions about&amp;nbsp;PHP or IIS7.&lt;/P&gt;
&lt;P&gt;Here it is, V2. Just copy/paste the following into a .bat file, right-click run it as elevated Administrator on Vista, and follow the prompted instructions. You should have PHP extracted into a directory of your choice before-hand (I favor and default to %SYSTEMDRIVE%\Inetpub\PHP for many aforementioned reasons; in my examples, I chose "C:\Program Files\P H P" to show it working with long pathnames).&lt;/P&gt;
&lt;P&gt;The batch script:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Works with PHP installed wherever, including pathnames with spaces. Just tell it where you installed PHP (sans double quotes and trailing backslash) 
&lt;LI&gt;Gives choice of whether to use the CGI or ISAPI version of PHP. You do have to give the correct binary name (php5isapi.dll or php-cgi.exe, assuming PHP5), but there are checks for that 
&lt;LI&gt;Gives warnings and errors if the directory/file does not exist, mismatched binary types, and additional steps you need to do to have a minimally functioning PHP&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Now, I am no PHP expert, so I&amp;nbsp;can only&amp;nbsp;give instructions for how to get PHP configured and running on IIS7. Questions about all other PHP-related features (like PHP extensions, integration with mySQL, etc) and how to get them working really&amp;nbsp;belong on a PHP support forum.&lt;/P&gt;
&lt;P&gt;Sample Execution Results:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Setting up PHP5 installed at "C:\Program Files\P H P", ISAPI version. Notice the defaults and verification of provided inputs.&lt;/LI&gt;&lt;/UL&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Inetpub\PHP
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

&lt;FONT color=#ff0000&gt;ERROR: PHP Binary "C:\Inetpub\PHP\php5isapi.dll" does not exist!
Please first completely extract PHP to "C:\Inetpub\PHP"
&lt;/FONT&gt;
Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:1

Press ENTER to accept [C:\Inetpub\PHP], or provide new value (folder path)
PHP Binaries Dir:&lt;FONT color=#ff0000&gt;C:\Program Files\P H P&lt;/FONT&gt;
Press ENTER to accept [ISAPI], or provide new value (CGI or ISAPI)
PHP Binary Type:
Press ENTER to accept [php5isapi.dll], or provide new value (filename)
PHP Binary Name:

David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Program Files\P H P
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:

Starting IIS7 Configuration...

Copying "C:\Program Files\P H P\PHP.INI-Recommended" to PHP.INI...
Setting PHP Handler...
CONFIG object "system.webServer/handlers" changed
Adding and Enabling PHP in ISAPI/CGI Restriction List...
CONFIG object "system.webServer/security/isapiCgiRestriction" changed

Finished IIS7 Configuration.

Test installation using PHP file content of:  &amp;lt;?php phpinfo();?&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;UL&gt;
&lt;LI&gt;Setting up PHP5 installed at "C:\Program Files\P H P", CGI version. Notice the defaults and reminders as you change to CGI.&lt;/LI&gt;&lt;/UL&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Inetpub\PHP
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

ERROR: PHP Binary "C:\Inetpub\PHP\php5isapi.dll" does not exist!
Please first completely extract PHP to "C:\Inetpub\PHP"

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:1

Press ENTER to accept [C:\Inetpub\PHP], or provide new value (folder path)
PHP Binaries Dir:&lt;FONT color=#ff0000&gt;C:\Program Files\P H P&lt;/FONT&gt;
Press ENTER to accept [ISAPI], or provide new value (CGI or ISAPI)
PHP Binary Type:&lt;FONT color=#ff0000&gt;cgi&lt;/FONT&gt;
Press ENTER to accept [php5isapi.dll], or provide new value (filename)
PHP Binary Name:&lt;FONT color=#ff0000&gt;php-cgi.exe&lt;/FONT&gt;

David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Program Files\P H P
PHP Binary Type  : cgi
PHP Binary Name  : php-cgi.exe
---------------------------------------------------------------------

Validating inputs...

&lt;FONT color=#ff0000&gt;ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "C:\Program Files\P H P\PHP.INI"&lt;/FONT&gt;

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:

Starting IIS7 Configuration...

Copying "C:\Program Files\P H P\PHP.INI-Recommended" to PHP.INI...
Setting PHP Handler...
CONFIG object "system.webServer/handlers" changed
Adding and Enabling PHP in ISAPI/CGI Restriction List...
CONFIG object "system.webServer/security/isapiCgiRestriction" changed

Finished IIS7 Configuration.

Test installation using PHP file content of:  &amp;lt;?php phpinfo();?&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;@IF ?%_ECHO%?==?? ECHO OFF

SETLOCAL
SET DIR_PHP_FROM=%SYSTEMDRIVE%\Inetpub\PHP
SET PHP_TYPE=ISAPI
SET PHP_MODULE=IsapiModule
SET PHP_BINARY=php5isapi.dll

:Menu
ECHO.
ECHO David.Wang's Sample PHP/IIS7 Configurator
ECHO Version: June 2006
ECHO.
ECHO ------------------------------ Summary ------------------------------
ECHO PHP Binaries Dir : %DIR_PHP_FROM%
ECHO PHP Binary Type  : %PHP_TYPE%
ECHO PHP Binary Name  : %PHP_BINARY%
ECHO ---------------------------------------------------------------------

REM
REM Do some basic validations
REM
ECHO.
ECHO Validating inputs...
IF /I ?%PHP_TYPE%? NEQ ?CGI? IF /I ?%PHP_TYPE%? NEQ ?ISAPI? ECHO.&amp;amp;ECHO ERROR: Binary Type MUST be either CGI or ISAPI
FOR %%I IN ( %PHP_BINARY% ) DO (
    IF /I ?%PHP_TYPE%? EQU ?CGI? IF /I ?%%~xI? NEQ ?.exe? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires a CGI EXE binary
    IF /I ?%PHP_TYPE%? EQU ?ISAPI? IF /I ?%%~xI? NEQ ?.dll? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires an ISAPI DLL binary
)
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
)
IF /I ?%PHP_TYPE%? EQU ?CGI? SET PHP_MODULE=CgiModule
IF /I ?%PHP_TYPE%? EQU ?CGI? ECHO.&amp;amp;ECHO ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "%DIR_PHP_FROM%\PHP.INI"
IF /I ?%PHP_BINARY%? NEQ ?php5isapi.dll? IF /I ?%PHP_BINARY%? NEQ ?php-cgi.exe? ECHO.&amp;amp;ECHO WARNING: Unrecognized PHP binary %PHP_BINARY%
ECHO.
ECHO Remember to tweak PHP.INI for security and functionality per php.net
ECHO Finished input validation.
ECHO.

SET GO=
SET /P GO=Press 1 to EDIT choices, or ENTER to start IIS modifications:
IF ?%GO%? EQU ?? GOTO :Start

ECHO.
ECHO Press ENTER to accept [%DIR_PHP_FROM%], or provide new value (folder path)
SET /P DIR_PHP_FROM=PHP Binaries Dir:
ECHO Press ENTER to accept [%PHP_TYPE%], or provide new value (CGI or ISAPI)
SET /P PHP_TYPE=PHP Binary Type:
ECHO Press ENTER to accept [%PHP_BINARY%], or provide new value (filename)
SET /P PHP_BINARY=PHP Binary Name:

GOTO :Menu

:Start
REM
REM Start Configuration
REM
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
    GOTO :EOF
)

ECHO.
ECHO Starting IIS7 Configuration...
ECHO.
ECHO Copying "%DIR_PHP_FROM%\PHP.INI-Recommended" to PHP.INI...
COPY /Y "%DIR_PHP_FROM%\PHP.INI-Recommended" "%DIR_PHP_FROM%\PHP.INI" &amp;gt;NUL

PUSHD %SYSTEMROOT%\System32\inetsrv

ECHO Setting PHP Handler...
APPCMD SET CONFIG -section:handlers "-+[name='PHP-%PHP_TYPE%',path='*.php',verb='GET,HEAD,POST',modules='%PHP_MODULE%',scriptProcessor='%DIR_PHP_FROM%\%PHP_BINARY%',resourceType='File']"

ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
APPCMD SET CONFIG -section:isapiCgiRestriction "-+[path='%DIR_PHP_FROM%\%PHP_BINARY%',allowed='true',groupId='PHP',description='PHP']"

POPD

ECHO.
ECHO Finished IIS7 Configuration.
ECHO.
ECHO Test installation using PHP file content of:  ^&amp;lt;?php phpinfo();?^&amp;gt;

ENDLOCAL&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=642354" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS+7.0+_2800_beta_2900_/default.aspx">IIS 7.0 (beta)</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>Be Weary of Stylish Fonts and Commandlines!</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/17/Be-Weary-of-Stylish-Fonts-and-Commandlines.aspx</link><pubDate>Sat, 17 Jun 2006 10:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:635048</guid><dc:creator>Anonymous</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/635048.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=635048</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=635048</wfw:comment><description>&lt;P&gt;I see variations of this common "stylish" mistake all the time... so I will highlight it this one time.&lt;/P&gt;
&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi,&lt;/P&gt;
&lt;P&gt;according to &lt;BR&gt;&lt;A href="http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/69a58513-141a-4adb-b6bc-2aaad4ea77b8.mspx?mfr=true"&gt;http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/69a58513-141a-4adb-b6bc-2aaad4ea77b8.mspx?mfr=true&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;I would like to trace the executing requests in iis6, but when I execute the&lt;BR&gt;logman start W3wpTrace â€“p â€œIIS: Request Monitorâ€ -ets command, I get the &lt;BR&gt;following error:&lt;/P&gt;
&lt;P&gt;C:\&amp;gt;logman start W3wpTrace -p "IIS: Request Monitor" -ets&lt;BR&gt;Unknown parameter "-p"&lt;BR&gt;Unknown parameter ""IIS:"&lt;BR&gt;Unknown parameter "Request"&lt;BR&gt;Unknown parameter "Monitor""&lt;/P&gt;
&lt;P&gt;The syntax of the command is incorrect.&lt;/P&gt;
&lt;P&gt;We are running windows server 2003 web edition Sp1&lt;/P&gt;
&lt;P&gt;Thanks.&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Ok, please don't strangle me, jump off a tall building, or thwap your forehead&amp;nbsp;after reading my explanation of this one (though a "doh" comment or acknowledgement would be nice). :-) Don't feel bad; this one has stumped many people.&lt;/P&gt;
&lt;P&gt;I think the URL you meant to reference is &lt;A href="http://technet2.microsoft.com/WindowsServer/en/Library/043ae04d-abcf-4eda-8d62-b5b8c633678b1033.mspx?mfr=true"&gt;this one&lt;/A&gt; because the one from the question merely led me to a generic overview page.&lt;/P&gt;
&lt;P&gt;In any case, your problem here is that the dash character in front of the "p" and the quote character&amp;nbsp;around "IIS: Request Monitor" are stylized dash and quote characters&amp;nbsp;and NOT the real ASCII dash (0x2D)&amp;nbsp;and quote (0x22)&amp;nbsp;used by logman.exe to delimit parameters and CMD shell to ignore space as parameter separator.&lt;/P&gt;
&lt;P&gt;Yes, I know that you copied that commandline directly from the web page and pasted onto the CMD shell, and yes it all LOOKS fine, but believe me, the web page content has stylized quotes and dashes which do not work on the commandline.&lt;/P&gt;
&lt;P&gt;Don't believe me? Verify it yourself. If you delete what appears to be the "dash" and "quote" characters and re-type the - and " characters on the keyboard onto the commandline, then I bet the command will work.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;Yes, I can work magic! Where's my consultation fee? ;-)&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The two tell-tale signs that tell me this "stylish" mistake happened?&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The errors mentioned "-p" and ""IIS" as problematic. Since the real ASCII quote character escapes spaces (while the stylized quote does not), the fact that "IIS was considered the incorrect parameter means that the " was not interpreted as the real ASCII quote. Likewise, the real ASCII dash character delimits parameters, and the fact -p was considered incorrect means that - was not interpreted as the real ASCII dash. 
&lt;LI&gt;The newsgroup post showed â€“p for "-p" and&amp;nbsp; â€œIIS: Request Monitorâ€&amp;nbsp; for "IIS: Request Monitor", suggesting the stylized quote got converted and EXPOSED on the newgroup. This was the obvious sign.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Yeah, sneaky CMD shell. It uses a font that disguises the stylized dash and quote to look exactly like the ASCII dash and quote. Well, now you know. And knowing is half the battle.&lt;/P&gt;
&lt;P&gt;Now, I consider this a bug in the documentation since commandlines should never use stylized quotes and dashes, but sigh, Documentation Writers use Word, which automatically changes things like this all the time... so it is a losing battle for me. Documentation Writers rarely use the commandline, so they have no idea how big of a mistake they are introducing. It is all just words and symbols to them, anyways.&lt;/P&gt;
&lt;P&gt;So, I see this stylish mistake happen&amp;nbsp;all the time, and I simply try to tell everyone about it.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=635048" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category></item><item><title>HOWTO: Enumerate IIS website and ftpsite configuration (VBScript using ADSI)</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/08/HOWTO-Enumerate-IIS-website-and-ftpsite-configuration-VBScript-using-ADSI.aspx</link><pubDate>Fri, 09 Jun 2006 07:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:623388</guid><dc:creator>Anonymous</dc:creator><slash:comments>23</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/623388.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=623388</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=623388</wfw:comment><description>&lt;P&gt;I recently got a request to make a script to illustrate how to enumerate useful values from both Web and FTP sites serviced by IIS, so I decided to make some additions to the original script tool located &lt;A href="http://blogs.msdn.com/david.wang/archive/2005/07/13/HOWTO_Enumerate_IIS_Website_Configuration.aspx"&gt;here&lt;/A&gt; to allow it to easily enumerate both Web and FTP sites.&lt;/P&gt;
&lt;P&gt;The modifications basically illustrate how both the Web and FTP services have consistent programming models and mechanisms to read/change configuration values. I basically only abstracted:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Metabase configuration node from /W3SVC (Web) to /MSFTPSVC (FTP) 
&lt;LI&gt;ADSI Class name from IIsWebServer (Web) to IIsFtpServer (FTP) 
&lt;LI&gt;Account for SecureBindings which exist for HTTPS (Web) but not for FTP&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;And the exact same script now displays both&amp;nbsp;FTP and Web site status. The new syntax is still pretty straight forward - I just added one new parameter to distinguish between querying FTP vs Web sites.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;EnumSites.vbs - enumerates status for Websites on this server 
&lt;LI&gt;EnumSites.vbs server1 - enumerates status for Websites on server1 
&lt;LI&gt;EnumSites.vbs&amp;nbsp;localhost FTP - enumerates status for Ftpsites on this server&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Enjoy,&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;OPTION EXPLICIT

DIM strServer, strServerType, strServerMetaType
DIM objService

strServer = "localhost"
strServerType = "Web"
strServerMetaType = "W3SVC"

IF WScript.Arguments.Length &amp;gt;= 1 THEN
    strServer = WScript.Arguments( 0 )
END IF

IF WScript.Arguments.Length = 2 THEN
    strServerType = WScript.Arguments( 1 )

    IF UCASE( strServerType ) = "FTP" THEN
        strServerType = "Ftp"
        strServerMetaType = "MSFTPSVC"
    ELSE
        strServerType = "Web"
        strServerMetaType = "W3SVC"
    END IF
END IF

WScript.Echo "Enumerating " &amp;amp; strServerType &amp;amp; "sites on " &amp;amp; strServer &amp;amp; VbCrLf
SET objService = GetObject( "IIS://" &amp;amp; strServer &amp;amp; "/" &amp;amp; strServerMetaType )
EnumServersites objService


SUB EnumServersites( objService )
    DIM objServer, strBindings

    FOR EACH objServer IN objService
        IF objServer.Class = "IIs" &amp;amp; strServerType &amp;amp; "Server" THEN
            WScript.Echo _
                "Site ID = " &amp;amp; objServer.Name &amp;amp; VbCrLf &amp;amp; _
                "Comment = """ &amp;amp; objServer.ServerComment &amp;amp; """ " &amp;amp; VbCrLf &amp;amp; _
                "State   = " &amp;amp; State2Desc( objServer.ServerState ) &amp;amp; VbCrLf &amp;amp; _
                "LogDir  = " &amp;amp; objServer.LogFileDirectory &amp;amp; _
                ""

            ' Enumerate the HTTP bindings (ServerBindings) and
            ' SSL bindings (SecureBindings) for HTTPS only
            strBindings = EnumBindings( objServer.ServerBindings )

            IF strServerType = "Web" THEN
                strBindings = strBindings &amp;amp; _
                EnumBindings( objServer.SecureBindings )
            END IF

            IF NOT strBindings = "" THEN
                WScript.Echo "IP Address" &amp;amp; VbTab &amp;amp; _
                             "Port" &amp;amp; VbTab &amp;amp; _
                             "Host" &amp;amp; VbCrLf &amp;amp; _
                             strBindings
            END IF
        END IF
    NEXT

END SUB

FUNCTION EnumBindings( objBindingList )
    DIM i, strIP, strPort, strHost
    DIM reBinding, reMatch, reMatches
    SET reBinding = NEW RegExp
    reBinding.Pattern = "([^:]*):([^:]*):(.*)"

    FOR i = LBOUND( objBindingList ) TO UBOUND( objBindingList )
        ' objBindingList( i ) is a string looking like IP:Port:Host
        SET reMatches = reBinding.Execute( objBindingList( i ) )
        FOR EACH reMatch in reMatches
            strIP = reMatch.SubMatches( 0 )
            strPort = reMatch.SubMatches( 1 )
            strHost = reMatch.SubMatches( 2 )

            ' Do some pretty processing
            IF strIP = "" THEN strIP = "All Unassigned"
            IF strHost = "" THEN strHost = "*"
            IF LEN( strIP ) &amp;lt; 8 THEN strIP = strIP &amp;amp; VbTab

            EnumBindings = EnumBindings &amp;amp; _
                           strIP &amp;amp; VbTab &amp;amp; _
                           strPort &amp;amp; VbTab &amp;amp; _
                           strHost &amp;amp; VbTab &amp;amp; _
                           ""
        NEXT

        EnumBindings = EnumBindings &amp;amp; VbCrLf
    NEXT

END FUNCTION

FUNCTION State2Desc( nState )
    SELECT CASE nState
    CASE 1
        State2Desc = "Starting (MD_SERVER_STATE_STARTING)"
    CASE 2
        State2Desc = "Started (MD_SERVER_STATE_STARTED)"
    CASE 3
        State2Desc = "Stopping (MD_SERVER_STATE_STOPPING)"
    CASE 4
        State2Desc = "Stopped (MD_SERVER_STATE_STOPPED)"
    CASE 5
        State2Desc = "Pausing (MD_SERVER_STATE_PAUSING)"
    CASE 6
        State2Desc = "Paused (MD_SERVER_STATE_PAUSED)"
    CASE 7
        State2Desc = "Continuing (MD_SERVER_STATE_CONTINUING)"
    CASE ELSE
        State2Desc = "Unknown state"
    END SELECT

END FUNCTION
&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=623388" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Access POST form data with ISAPI</title><link>http://blogs.msdn.com/david.wang/archive/2006/05/10/HOWTO-Access-POST-form-data-with-ISAPI.aspx</link><pubDate>Thu, 11 May 2006 09:47:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:595206</guid><dc:creator>Anonymous</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/595206.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=595206</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=595206</wfw:comment><description>&lt;P&gt;This is a frequently asked question about IIS6 extensibility - how to access the request entity body on the way in - as well as how to configure IIS.&lt;/P&gt;
&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;I want to catch all incoming requests, add some header and watch it when its out. Therefore I used a filter which can't be use alone in IIS 6 cause of the post data and so I added the wildcards. I'm using the same DLL for both.&lt;/P&gt;
&lt;P&gt;Since I'm interested in all incoming requests I thought its best to define a global filter and ScriptMap.&lt;/P&gt;
&lt;P&gt;What do you think ?&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;I am just going to give the logical answers now. At the moment, I am a little short on time, so I cannot post code samples showing how to do this with ISAPI Filter and Extension (yes, I write and test my code before publishing it publicly - you guys do want functional and correct code samples, not merely code I whipped together on the side, right? ;-) ). However, if you really want the code samples, you can ask via blog entry comments for the code sample, and I will see what I can do and link it in...&lt;/P&gt;
&lt;P&gt;There are two ways to access request entity body with ISAPI on IIS6:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;ISAPI Filter subscribing to SF_NOTIFY_READ_RAW_DATA and configured as a Global ISAPI Filter, which runs on all requests. It requires:&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Run IIS6 in IIS5 Compatibility Mode (you lose tremendous benefits of Application Pools and process/application isolation)&lt;/LI&gt;
&lt;LI&gt;Since SF_NOTIFY_READ_RAW_DATA is a streaming event (i.e. it triggers on every Network read), the ISAPI code must buffer and parse that data (including de-chunking) at an HTTP level to determine what is "request entity body POSTed by an HTML FORM"&lt;/LI&gt;
&lt;LI&gt;In general, this parsing is non-trivial for an ISAPI to do correctly 100% of the time and comes with severe caveats, such as you cannot do this over SSL (request buffering in SF_NOTIFY_READ_RAW_DATA is not compatible with SSL). See this &lt;A HREF="/david.wang/archive/2006/04/07/IIS6_and_HTTP_Server_API_Part_1.aspx"&gt;blog entry&lt;/A&gt; for more related details&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;ISAPI Extension calling HSE_REQ_EXEC_URL and configured as a wildcard application mapping. There are no difficult requirements, but since it is an application mapping which operates at a different point in request processing versus ISAPI Filters (see this &lt;A HREF="/david.wang/archive/2006/04/28/HOWTO_Run_Console_Applications_from_IIS6_on_Windows_Server_2003_Part_2.aspx"&gt;blog entry&lt;/A&gt; for an end-to-end view), you have to be aware of the resulting difference in expected behavior.&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Application Mappings are invoked ONCE per request. You do not get a callback on every Network read. Thus, it does NOT offer streaming access to data (i.e. you get exactly once chance to read/manipulate the entity body prior to passing it on to the child request)&lt;BR&gt;&lt;BR&gt;This means that to manipulate large entity body, you HAVE to buffer it all in memory before manipulating it, and if you do not want to truncate the entity body, you HAVE to pass the buffer on to the child request on invoking HSE_REQ_EXEC_URL&lt;/LI&gt;
&lt;LI&gt;Since ISAPI Extension operates an an application-level, you know that you get "request entity body POSTed by an HTML FORM" when you read ECB-&amp;gt;lpbData for the pre-buffered data or call ReadClient() to get remaining data. No parsing, decrypting, or de-chunking required&lt;/LI&gt;
&lt;LI&gt;This method works fine with SSL&lt;/LI&gt;
&lt;LI&gt;The URL of the request determines the effective Application Mappings (including wildcard) of the request. Thus, it does not matter if you configure a wildcard application mapping at the global W3SVC/ScriptMaps level - if a child node has an overriding ScriptMaps setting which does NOT contain your wildcard application mapping, it will NOT execute for requests whose effective metadata comes from that overriding ScriptMaps setting.&lt;BR&gt;&lt;BR&gt;In other words, unlike Global ISAPI Filter which reliably triggers on every request, a "global" wildcard application mapping can be silenced by a child URL whose effective ScriptMaps property does NOT include that wildcard application mapping.&lt;/LI&gt;&lt;/UL&gt;&lt;/OL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=595206" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Rename the Computer running IIS6</title><link>http://blogs.msdn.com/david.wang/archive/2006/05/01/HOWTO-Rename-the-Computer-running-IIS6.aspx</link><pubDate>Mon, 01 May 2006 12:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:587440</guid><dc:creator>Anonymous</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/587440.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=587440</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=587440</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="Courier new" color=#008000&gt;
&lt;P&gt;Hi ,&lt;/P&gt;
&lt;P&gt;I have two web servers. One web server(w2k3Ent) and IIS 6.0 in a work group and One identical Web Server in the Domain. I want to now rename the Server names of the both, to comply some policy. Will the IIS functionality be affected because the IUSR accounts still be left with old computer names, can I go ahead and rename them as well.&lt;/P&gt;
&lt;P&gt;Any Microsoft links and help is appreciated&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Well, if you are merely interested in Microsoft links on the question, then you should just search for them. I punched in "Rename IIS Computer Name site:support.microsoft.com" in my favorite search engine, and it came up with tons of hits. I trust you know how to do this yourself and sort through the noise - it is a necessary skill in this day and age.&lt;/P&gt;
&lt;P&gt;By design, Microsoft links for support/troubleshooting assume the user knows nothing, so they optimize towards keyword/pattern matching which results in some resolution instruction steps that tell you HOW to resolve that pattern with little explanation of WHY. If you pattern match incorrectly, the resolution may not apply and may/not harm you. If you do not follow the instructions correctly, the resolution may not apply and may/not harm you.&lt;/P&gt;
&lt;P&gt;What I am going to talk about are the details behind the scenes of what is going on because I trust that when someone understands the dependencies and what is actually going on, they can figure out how to do the right things themselves or even correctly react to unexpected things since everyone's system is unique.&lt;/P&gt;
&lt;P&gt;Give a man a fish, and you feed him that night. Teach a man to fish, and he feeds himself forever.&lt;/P&gt;
&lt;H4&gt;About the Computer Rename...&lt;/H4&gt;
&lt;P&gt;IIS itself does not care about the name of the user accounts used for purposes like:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Anonymous authentication - no, it does NOT mean that everyone automatically has access. See this &lt;A href="http://blogs.msdn.com/david.wang/archive/2005/05/27/Access_Denied_to_Administrators_or_Anonymous_User.aspx"&gt;blog entry&lt;/A&gt; for details. 
&lt;LI&gt;UNC Vdir Access 
&lt;LI&gt;Application Pool Identity 
&lt;LI&gt;COM+ Application Identity&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Therefore, IIS definitely&amp;nbsp;functions after renaming the computer. The question is whether all OTHER dependent applications running on or related to IIS are kosher with renaming the computer.&lt;/P&gt;
&lt;P&gt;For examples of the hassles, see &lt;A href="http://support.microsoft.com/?id=234142"&gt;KB 234142&lt;/A&gt; on renaming a NT4 server running IIS4 (I know, it is not applicable here, but you wanted details, right?)&lt;/P&gt;
&lt;P&gt;Or, you may be running bad/broken applications which ASSUME that the username of the IIS anonymous user is based on the machine name (i.e. they assume the anonymous user's name is IUSR_&amp;lt;NewMachineName&amp;gt; instead of reading the value from IIS configuration). These applications will obviously be broken after the computer rename, and you will have to determine how to address that - either fix the application or change the anonymous user's name to match the broken assumption.&lt;/P&gt;
&lt;P&gt;Now, if you DECIDE for whatever reason&amp;nbsp;to rename the user accounts used by IIS after renaming the computer (remember, IIS does not need you to do anything), you need to be aware of how Windows user accounts work that can affect IIS behavior:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;If you create a NEW user account for use in IIS (as the anonymous user, for example), its SID will be DIFFERENT than the original anonymous user. 
&lt;UL&gt;
&lt;LI&gt;Since files are ACL'd by SID and NOT by username, you will have to re-ACL files/directories EVERYWHERE to re-secure against anonymous access. 
&lt;LI&gt;You will also have to change IIS configuration to use the new user name since IIS stores and uses the username and&amp;nbsp;NOT the SID for its user accounts.&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;If you RENAME the existing user account in Windows to a new name, the SID stays the same, so you do not need to re-ACL anything... but you still need to change IIS configuration to use the new user name.&lt;/LI&gt;&lt;/OL&gt;
&lt;H4&gt;Some Helpful Scripts&lt;/H4&gt;
&lt;P&gt;Now, I do not have a script you can run as Administratior which re-ACLs files one username had access to another username... and I am not going to tackle that problem here because that is way outside the scope of IIS. Find your favorite batch/scripting support group for a solution to this one.&lt;/P&gt;
&lt;P&gt;However, I do have a script named "RenameIISUser.bat" to do the IIS configuration changes from old name to new name. See the end of this blog entry.&lt;/P&gt;
&lt;P&gt;For safety purposes, the script only displays what it will do. You can inspect the output to see if it is OK and then copy/paste the commands yourself, or you can remove &lt;CODE&gt;SET DEBUG=ECHO&lt;/CODE&gt; to have the script take action.&lt;/P&gt;
&lt;P&gt;I also have a script called &lt;A href="http://blogs.msdn.com/david.wang/archive/2005/12/07/HOWTO_Synchronize_User_Credentials_in_IIS.aspx"&gt;SyncIISUser.bat&lt;/A&gt; that allows you sync user account passwords in IIS configuration assuming you know the username.&lt;/P&gt;
&lt;P&gt;So, if you happen to rename the user account AND change its password, you can first run the script at the end of this blog entry to change the username in IIS configuration, and then run the other script to&amp;nbsp;associate&amp;nbsp;the new password with that username in IIS configuration.&lt;/P&gt;
&lt;P&gt;For example, suppose you renamed IUSR_MachineName used for Anonymous Authentication to "New User Name" and also changed its password to "New Password". You would run the following commands, in order, to fix your IIS configuration:&lt;/P&gt;&lt;PRE&gt;RenameIISUser.bat Anonymous "IUSR_%COMPUTERNAME%" "New User Name"
SyncIISUser.bat Anonymous "New User Name" "New Password"&lt;/PRE&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;@IF ?%_ECHO%?==?? ECHO OFF
SETLOCAL
SET CMD_ADSUTIL=CSCRIPT %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs
SET DEBUG=ECHO

IF ?%1? EQU ?? GOTO :Help
IF ?%2? EQU ?? GOTO :Help

IF /I ?%1? EQU ?Anonymous? SET PROPERTY_TO_FIND=AnonymousUserName
IF /I ?%1? EQU ?WAM?       SET PROPERTY_TO_FIND=WAMUserName
IF /I ?%1? EQU ?UNC?       SET PROPERTY_TO_FIND=UNCUserName

SET USERNAME_TO_MATCH=%2
SET NEW_PROPERTY_VALUE=%3

FOR /F "usebackq skip=1 tokens=*" %%I IN ( `%CMD_ADSUTIL% FIND %PROPERTY_TO_FIND%` ) DO (
    FOR /F "usebackq tokens=3,* delims= " %%J IN ( `%CMD_ADSUTIL% GET "%%I/%PROPERTY_TO_FIND%"` ) DO (
        IF /I ?%USERNAME_TO_MATCH%? EQU ?%%K? (
            %DEBUG% %CMD_ADSUTIL% SET "%%I/%PROPERTY_TO_FIND%" %NEW_PROPERTY_VALUE%
        )
    )
)

ENDLOCAL

GOTO :EOF



REM
REM Display syntax
REM
:Help
ECHO %~n0 {Anonymous^|WAM^|UNC} "UserName" "New UserName"
ECHO.
ECHO Find all [Anonymous^|WAM^|UNC] user accounts matching "UserName" in
ECHO IIS configuration and change it to "New UserName"
ECHO.&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=587440" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Run Console Applications from IIS6 on Windows Server 2003, Part 2</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/28/HOWTO-Run-Console-Applications-from-IIS6-on-Windows-Server-2003-Part-2.aspx</link><pubDate>Fri, 28 Apr 2006 14:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:585688</guid><dc:creator>Anonymous</dc:creator><slash:comments>44</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/585688.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=585688</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=585688</wfw:comment><description>&lt;P&gt;I finally have enough blog entries about various portions of IIS6 request processing that I can stitch together&amp;nbsp;this meta-blog-entry explaining how it all works together and then apply it towards an issue. You probably want to keep a link to this loaded entry. Anyhow, here goes...&lt;/P&gt;
&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;We currently has IIS 6 installed on a Windows 2003 server, and we are attempting to run some executables through the browser. We have "Scripts and Executables" selected for the web application, and we are currently allowing "All Unknown CGI Extensions" and "All Unknown ISAPI Extensions" under Web Service Extensions during testing.&lt;/P&gt;
&lt;P&gt;With this setup we are able to run some executables successfully through a browser, like:&lt;/P&gt;
&lt;P&gt;Ping&lt;BR&gt;Hostname&lt;BR&gt;netstat&lt;BR&gt;query session&lt;/P&gt;
&lt;P&gt;However other executables are not working:&lt;/P&gt;
&lt;P&gt;Net&lt;BR&gt;Dirquota&lt;BR&gt;Filescrn&lt;BR&gt;Fsutil quota&lt;/P&gt;
&lt;P&gt;We are getting no value for most of these, except "Fsutil quota" which is returning:&lt;/P&gt;
&lt;P&gt;The FSUTIL utility requires that you have administrative privileges&lt;/P&gt;
&lt;P&gt;I have confirmed that the browser is using an ID that has administrative privileges by returning the userid it is using. This administrative ID does not have any problem running Fsutil quota on the server directly.&lt;/P&gt;
&lt;P&gt;What privileges am I missing? Does something else need to be set up, or is it possible the executables not working are not CGI or ISAPI?&lt;/P&gt;
&lt;P&gt;Thanks for any help.&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Ok, this blog entry is unique in that it is the first time that I am aggregating&amp;nbsp;information from all my prior posts and applying it to resolve a problem. I will NOT explain everything in detail - only the new stuff... for everything else, if I pulled the information from a blog entry, it will be linked in this blog entry somewhere appropriate.&lt;/P&gt;
&lt;H4&gt;Putting it all together&lt;/H4&gt;
&lt;P&gt;When it comes to request execution on IIS6, this is how I think about things:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;HTTP.SYS parses HTTP requests in kernel mode and sends a digested form to user mode w3wp.exe. Interesting details about this process can be found in this &lt;A href="/david.wang/archive/2006/04/07/IIS6_and_HTTP_Server_API_Part_1.aspx"&gt;blog entry&lt;/A&gt;. 
&lt;LI&gt;IIS6 in user mode w3wp.exe receives this request and&amp;nbsp;determines the URL namespace (and hence all applicable metadata from configuration that affect the execution of this request - which includes the authentication required, the ScriptMaps applicable for the request, etc) 
&lt;LI&gt;Then, the authentication process runs to determine the user token used to execute a request.&lt;BR&gt;&lt;BR&gt;One of the factors that affect a user token's abilities is the way the user logon was performed (i.e. logon type). In other words, Interactive logon from Remote Desktop/Local Console is different than the Network logon performed by IIS6. This &lt;A href="/david.wang/archive/2006/04/09/HOWTO_Run_Console_Applications_from_IIS6_on_Windows_Server_2003.aspx"&gt;blog entry&lt;/A&gt;&amp;nbsp;describes the differences in more detail.&lt;BR&gt;&lt;BR&gt;Furthermore, IIS6 can perform different logon types depending on authentication protocol as well as by configuration, though the defaults pretty much work and should not be fiddled with.&lt;BR&gt;&lt;BR&gt;So, logon difference alone may affect how FSUTIL.EXE behaves. 
&lt;LI&gt;In addition to authentication producing a user token, IIS has to determine the correct handler to execute the requested resource&amp;nbsp;(i.e. should the resource be handled as a static file and sent back as-is? or should it go to a CGI/ISAPI to process? or should it produce a courtesy redirect? Etc). This &lt;A href="/david.wang/archive/2005/10/14/HOWTO_IIS_6_Request_Processing_Basics_Part_1.aspx"&gt;blog entry&lt;/A&gt; and this &lt;A href="/david.wang/archive/2005/07/11/Allow_file_downloads_on_IIS_6.aspx"&gt;blog entry&lt;/A&gt; describes that decision process. 
&lt;LI&gt;After determining the specific type of handler to execute a request, IIS uses the user token to execute the handler for the request. This &lt;A href="/david.wang/archive/2005/06/29/IIS_User_Identity_to_Run_Code_Part_2.aspx"&gt;blog entry&lt;/A&gt; describes how IIS selects the user token to execute the handler. 
&lt;LI&gt;Additional user code can be run by the handler, which have arbitrary behavior when it comes to user token, privileges, and generated response 
&lt;LI&gt;The browser client can have automatic behavior which affects the number and sequence of requests made to IIS, as described in this &lt;A href="/david.wang/archive/2006/04/26/Why_Clicking_a_hyperlink_can_result_in_multiple_requests_to_web_servers.aspx"&gt;blog entry&lt;/A&gt;.&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Whew. If you got to here and read through all the associated blog entries, you have just gone through a LOT of details about how IIS6 functions to process requests as well as how it interacts with user identity. Time to put this info to good use and apply it. :-)&lt;/P&gt;
&lt;H4&gt;The Problem Restated...&lt;/H4&gt;
&lt;P&gt;Ok, the following details and conclusions all come from the aforementioned blog entries. If you get lost, I suggest you read 'em and read 'em again until you understand it. :-)&lt;/P&gt;
&lt;P&gt;The first thing that needs to be clear is HOW you are "running executables through the browser". Since IIS&amp;nbsp;only recognizes static files, CGI/ISAPI, or Scripts (ok, I am simplifying DAV away since it is not involved in this picture), there are two basic ways to run executables:&lt;/P&gt;
&lt;H5&gt;As a CGI/ISAPI&lt;/H5&gt;
&lt;P&gt;If you want IIS to directly run the executable as a CGI or ISAPI, then you need to configure "Scripts and Executables" execute permission as well as Web Service Extension for the binary. i.e.&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;/cgi-bin has "Scripts and Executable" execute permission enabled. 
&lt;LI&gt;&amp;lt;full-path-to-FSUTIL.EXE&amp;gt;\FSUTIL.EXE is enabled as a Web Service Extension. 
&lt;LI&gt;You make a request to &lt;A href="http://localhost/cgi-bin/FSUTIL.EXE"&gt;http://localhost/cgi-bin/FSUTIL.EXE&lt;/A&gt; 
&lt;LI&gt;IIS is going to execute FSUTIL.EXE as a CGI, so it checks it against Web Service Extension. It is allowed. 
&lt;LI&gt;Thus, IIS will execute FSUTIL.EXE as a CGI EXE using the user token obtained through whatever authentication protocol is negotiated between the browser and server. 
&lt;LI&gt;The EXE is executed using either CreateProcess() or CreateProcessAsUser() Win32 API call, depending on IIS configuration for CGI execution. If it is CreateProcess(), then&amp;nbsp;FSUTIL.EXE runs with the&amp;nbsp;Process Identity; if it is CreateProcessAsUser(), then FSUTIL.EXE runs with the IIS impersonated identity. 
&lt;LI&gt;IIS parses the&amp;nbsp;text output from FSUTIL.EXE according to CGI specification and then sends a response back to the client.&lt;BR&gt;&lt;BR&gt;If you use the nph- prefix, IIS won't parse the text and just send it back as is. Of course, if you happen to send invalid HTTP response using NPH, the client can complain or behave weirdly...&lt;/LI&gt;&lt;/OL&gt;
&lt;H5&gt;Via a Scriptmapped CGI/ISAPI&lt;/H5&gt;
&lt;P&gt;If you want to run executables on IIS from a script (i.e. an ASP, ASP.Net, or PHP page is considered a script resource executed by ASP.DLL, ASPNET_ISAPI.DLL, or PHP-CGI.EXE / PHPISAPI.DLL Script Engine, respectively), then you need to configure "Scripts" execute permission as well as Web Service Extension for the appropriate Script Engine. i.e.&lt;/P&gt;
&lt;P&gt;MyScript.asp contains the following content which executes FSUTIL.EXE:&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;&amp;lt;%
set objShell = Server.CreateObject( "WScript.Shell" )
objShell.Run( "FSUTIL.EXE" )
%&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;OL&gt;
&lt;LI&gt;/cgi-bin has "Scripts" execute permission enabled. 
&lt;LI&gt;%systemroot%\System32\inetsrv\ASP.DLL is enabled as a Web Service Extension. 
&lt;LI&gt;/cgi-bin has a ScriptMaps property which associates .asp extension to %systemroot%\System32\inetsrv\ASP.DLL as a Script Engine. 
&lt;LI&gt;You make a request to &lt;A href="http://localhost/cgi-bin/MyScript.asp"&gt;http://localhost/cgi-bin/MyScript.asp&lt;/A&gt; 
&lt;LI&gt;IIS identifies ASP.DLL as the ISAPI Script Engine to process the /cgi-bin/MyScript.asp resource and checks it against Web Service Extension. Since it is allowed, it executes ASP.DLL using the user token obtained through whatever authentication protocol is negotiated between the browser and server.&lt;BR&gt;&lt;BR&gt;Note: even though the ASP page runs FSUTIL.EXE, FSUTIL.EXE does NOT need to be in Web Service Extension because IIS never runs nor knows about FSUTIL.EXE. IIS only knows it is running ASP.DLL so that is what needs to be enabled as a Web Service Extension. 
&lt;LI&gt;ASP.DLL will keep the impersonated identity from IIS and parse/execute the script code in MyScript.asp using Windows Scripting Host. objShell.Run() translates into a CreateProcess() Win32 API call, and FSUTIL.EXE runs using the Process Identity (this is how CreateProcess is documented to work!) 
&lt;LI&gt;FSUTIL output is unknown to ASP (and IIS)&amp;nbsp;unless you capture the output of objShell.Run() somehow and then Response.Write() it so that IIS knows about it.&lt;/LI&gt;&lt;/OL&gt;
&lt;H4&gt;Ok, back to the Original Problem&lt;/H4&gt;
&lt;P&gt;At this point, a couple differences should jump out at you:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;When you ran FSUTIL QUOTA directly, it was executed by a user&amp;nbsp;token with Interactive logon type. When you ran it from IIS, the user token has Network logon type. 
&lt;LI&gt;Depending on how you ran the executables, FSUTIL.EXE was either called via CreateProcess() or CreateProcessAsUser(). The critical difference between them is that CreateProcess() implicitly uses the Process Identity to create the new FSUTIL.EXE process, while CreateProcessAsUser() uses the impersonated identity obtained by IIS through authentication.&lt;BR&gt;&lt;BR&gt;The Application Pool Identity of the Application Pool servicing the URL namespace that executed the request determines the Process Identity&lt;BR&gt;&lt;BR&gt;The authentication protocol negotiated between the browser and server determines the impersonated identity, in a manner consistent with this &lt;A href="/david.wang/archive/2005/06/29/IIS_User_Identity_to_Run_Code_Part_2.aspx"&gt;blog entry&lt;/A&gt;.&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;I do not know if FSUTIL.EXE has special code that cares about the above details. But I do know that CMD.EXE does... so YMMV.&lt;/P&gt;
&lt;H4&gt;What about the Executables which appear to return Nothing&lt;/H4&gt;
&lt;P&gt;As for the question of whether the executables "not working" are not CGI/ISAPI... After executing a request handler, IIS expects a valid HTTP response to come back so that it can send it back to the requesting client.&lt;/P&gt;
&lt;P&gt;CGI is nothing more than an executable which outputs text which conforms to the CGI specification (this &lt;A href="/david.wang/archive/2006/04/20/HOWTO_Retrieve_Request_Headers_using_ISAPI_ASP_and_ASP_Net.aspx"&gt;blog entry&lt;/A&gt; describes an interest interaction between CGI and HTTP). If the output conforms to specification, IIS sends it back as-is; otherwise, IIS returns a 502 response.&lt;/P&gt;
&lt;P&gt;Since IIS6 in WS03SP1 is lax about CGI conformance and no longer requires the status: and content-type headers, if the executable happens to output a CRLF before printing data, that data gets interpreted as response entity by IIS and sent back to the client as-is. The following are some illustrative examples with CRLF clearly marked as \r\n.&lt;/P&gt;
&lt;H5&gt;NET.EXE Output:&lt;/H5&gt;&lt;PRE&gt;&lt;FONT color=#ff0000&gt;The syntax of this command is:\r\n&lt;/FONT&gt;
\r\n
\r\n
NET [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |\r\n
      HELPMSG | LOCALGROUP | NAME | PAUSE | PRINT | SEND | SESSION |\r\n
      SHARE | START | STATISTICS | STOP | TIME | USE | USER | VIEW ]\r\n&lt;/PRE&gt;
&lt;H5&gt;PING.EXE Output:&lt;/H5&gt;&lt;PRE&gt;\r\n
Active Connections\r\n
\r\n
  Proto  Local Address          Foreign Address        State\r\n
  TCP    TEST-MACHINE:80        0.0.0.0:80             ESTABLISHED\r\n&lt;/PRE&gt;
&lt;P&gt;As you can see, NET.EXE fails because its CGI output&amp;nbsp;starts off with what looks like an invalid header named "The syntax of this command is:" (it has spaces in it, and no header value) prior to the double CRLF, so it fails to parse correctly and IIS returns a 502 "Bad Gateway" response. Meanwhile, PING.EXE succeeds because it starts off with a CRLF that tells IIS to use a 200 OK response header ahead of the PING output.&lt;/P&gt;
&lt;P&gt;Now, if you prefix the resources with NPH- (i.e. nph-netstat.exe or nph-ping.exe), then IIS will NOT process the CGI output and just send it as-is back to the client. That is what NPH means - Non-Parsed Header. Of course, this avoids the 502 response from IIS, but if the output is not proper HTTP, it will now confuse the client, which can have its own arbitrary behavior...&lt;/P&gt;
&lt;P&gt;Basically, these console commands are not designed to produce output that conforms to CGI specification, so the fact that they work when invoked as CGI by IIS is purely random, mostly depending on whether the output&amp;nbsp;begins with something that looks like broken HTTP headers or not.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;I congratulate you on getting to the end of this marathon of a blog post. I finally got a juicy topic to explain and a lot of blog entries to logically stitch together. :-)&lt;/P&gt;
&lt;P&gt;Hopefully, this clears up common confusions surrounding:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;IIS request execution logic 
&lt;LI&gt;User identity used to execute a request 
&lt;LI&gt;Executing an EXE via CreateProcess() is different than via CreateProcessAsUser(), regardless if you made IIS directly execute it as a CGI or indirectly through a Script 
&lt;LI&gt;Executing an ISAPI uses the impersonated identity by default, but can be the Process Identity, depending on the ISAPI 
&lt;LI&gt;How some EXEs magically work as "CGIs" but others fail 
&lt;LI&gt;How some programs, like CMD.EXE, simply fail depending on whether invoked via CreateProcess() or CreateProcessAsUser()... and maybe FSUTIL.EXE is in the same category&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=585688" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>Why Clicking a hyperlink can result in multiple requests to web server(s)</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/26/Why-Clicking-a-hyperlink-can-result-in-multiple-requests-to-web-servers.aspx</link><pubDate>Wed, 26 Apr 2006 15:56:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:584086</guid><dc:creator>Anonymous</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/584086.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=584086</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=584086</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Dear Experts,&lt;/P&gt;
&lt;P&gt;If you could share your opinions or point out some reference links, I do appreciate!&lt;/P&gt;
&lt;P&gt;My web server: IIS and SharePoint Portal server 2003&lt;/P&gt;
&lt;P&gt;My qestion is: After I click one hyperlink in a page (ASP.NET site) using IE, why there are 3 requests recorded in the IIS log file?&lt;/P&gt;
&lt;P&gt;Example:&lt;BR&gt;2006-04-25 20:16:54 (IP) GET /_layouts/1033/Viewer.aspx&lt;BR&gt;contentId=acbdef.htm 80 - (IP)&lt;BR&gt;Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.2;+SV1;+.NET+CLR+1.1.4322)&lt;BR&gt;SITESERVER=ID=(server_id) &lt;A href="http://localhost/Manager.aspx"&gt;http://localhost/Manager.aspx&lt;/A&gt; 401 2&lt;BR&gt;2148074254 1912 15&lt;/P&gt;
&lt;P&gt;2006-04-25 20:17:02 (IP) GET /_layouts/1033/Viewer.aspx&lt;BR&gt;contentId=acbdef.htm 80 - (IP)&lt;BR&gt;Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.2;+SV1;+.NET+CLR+1.1.4322)&lt;BR&gt;SITESERVER=ID=(server_id) &lt;A href="http://localhost/Manager.aspx"&gt;http://localhost/Manager.aspx&lt;/A&gt; 401 1 0 2148 15&lt;/P&gt;
&lt;P&gt;2006-04-25 20:17:20 (IP) GET /_layouts/1033/Viewer.aspx&lt;BR&gt;contentId=acbdef.htm 80 (domain)\(my_id) (IP)&lt;BR&gt;Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.2;+SV1;+.NET+CLR+1.1.4322)&lt;BR&gt;SITESERVER=ID=(server_id) &lt;A href="http://localhost/Manager.aspx"&gt;http://localhost/Manager.aspx&lt;/A&gt; 302 0 0 835&lt;BR&gt;17671&lt;/P&gt;
&lt;P&gt;Thank you very much!&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;The IIS log file contains three entries because the browser actually made three requests on behalf of your single click of a hyperlink. Modern browsers are "smart" and do many things "behind the scenes" to ease the end-user experience.&lt;/P&gt;
&lt;P&gt;In other words, you cannot assume that every click of a hyperlink translates into exactly one request to a web server.&lt;/P&gt;
&lt;P&gt;Here are same examples of situations where you make one logical click of a hyperlink but the web browser makes multiple requests to web server(s) to&amp;nbsp;satisfy the request.&lt;/P&gt;
&lt;P&gt;Consider what happens when you click on a link to...&lt;/P&gt;
&lt;H4&gt;An HTML page which includes&amp;nbsp;links to Pictures&lt;/H4&gt;
&lt;P&gt;You may have only clicked on the link to the HTML page, but the browser parses the HTML page, notices 10 pictures in IMG tags, makes 10 additional requests to web server(s) to retrieve those resources, and&amp;nbsp;finally renders the HTML page composed with those pictures.&lt;/P&gt;
&lt;P&gt;Similar behavior happens for referenced resources of an HTML page like CSS Stylesheets, client-side Javascript, etc... because the browser must retrieve those URL resources to properly render a given HTML page.&lt;/P&gt;
&lt;P&gt;Clearly, to satisfy you making a single click of a hyperlink pointing to a single HTML page, the browser has to make MANY requests on your behalf to give you a desirable user experience of a properly rendered HTML page.&lt;/P&gt;
&lt;H4&gt;An HTML page that results in a 302 Redirection&lt;/H4&gt;
&lt;P&gt;You may have only clicked on the link which causes a 302 redirection response to be returned, but do you expect the browser to:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Ask you whether to follow the 302 redirection? 
&lt;LI&gt;Automatically make a new request to follow the 302 redirection assuming it is "safe"... and continue following the 302 redirections, automatically, until a non-redirection response is retrieved&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Most browser implementations default to #2, which means that for a single click, the web browser may automatically make MANY requests on your behalf, trasparently, to resolve through the redirections.&lt;/P&gt;
&lt;P&gt;Users expect browsers to follow redirections and render the final result, not repeatedly ask for permission to follow redirections and not render the end result.&lt;/P&gt;
&lt;H4&gt;An HTML page that requires Authentication&lt;/H4&gt;
&lt;P&gt;You may have only clicked on the link which requires authentication, but do you expect the browser to:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Show you the 401 response since the URL requires authentication? 
&lt;LI&gt;Immediately show you the username/password dialog? 
&lt;LI&gt;Depending on security settings, either automatically attempt authentication via acceptable protocols to the web server or show the username/password dialog, correctly authenticate to the web server, and retrieve and display the secured resource?&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Most browser implementations default to #3, which means that for a single click of a hyperlink to a resource that requires authentication, the web browser may automatically make additional requests and interpret their responses to negotiate authentication with the web server in reaction to getting a 401 response with WWW-Authenticate headers.&lt;/P&gt;
&lt;P&gt;Users expect browsers to eliminate as many of the username/password popup dialogs as possible by automatically making additional requests to authenticate and retrieve the resource for them wherever it makes configured security sense... and not just repeatedly ask for user credentials to access resources.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;In your case, you are observing the fact that:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The&amp;nbsp;/Manager.aspx resource requires authentication 
&lt;LI&gt;Thus, when you clicked on the link, it&amp;nbsp;made the browser make an anonymous request to the server, and it got a 401.2 response back.&lt;BR&gt;&lt;BR&gt;Making an anonymous request is the default behavior for a browser and makes sense. A browser has no way to know WHAT authentication protocols a given website requires BEFORE making that first request (software&amp;nbsp;cannot tell the future... yet ;-) ), so the&amp;nbsp;most obvious choice is to make an anonymous request and see what the web server does in response. 
&lt;LI&gt;Since the website is in a security zone that allows the web browser to automatically authenticate,&amp;nbsp;the web browser&amp;nbsp;attempts to authenticate using the specified protocol(s) of the web server. My guess is that only Integrated Authentication is enabled on the website containing /Manager.aspx, so you see the second leg of that protocol negotiation sequence and a 401.1 response
&lt;LI&gt;Finally, the client successfully authenticates with IIS and IIS starts to execute the /Manager.aspx resource (no more 401 errors logged), but that resource actually ends up sending a 302 redirection ... so a 302 Redirection response is sent back to the client. At this point, the browser probably automatically follows that redirection to something else, and that process continues.&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Hopefully, you now see that Modern browsers do many things transparently to make the end-user's life incredibly simple and easy - simple, logical actions that you do with a single click, like browsing a normal HTML page with pictures, or accessing an authenticated URL, or following a 302 redirection... all require additional requests and negotiation logic that the browser transparently performs on your behalf.&lt;/P&gt;
&lt;P&gt;Thus, one may be surprised to see that a single logical click of a hyperlink results in multiple requests to servers... but I can only say that it is perfectly normal. :-)&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=584086" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category></item></channel></rss>