A Runtime in Every Pot
As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.
Jay Allen, Mark Davis, Heidi Housten, and Dan Mohr
Microsoft Corporation
October 2, 2001
We haven't felt much like kidding about this month, so we just got down to brass tacks and answered questions. We received fewer questions this month and got the impression that we weren't the only ones with heavier issues in our hearts and on our minds.
We have quite a feast for you this month. We have a taste of Microsoft®.NET and we show you how to implement popular Perl functionality to name a couple of dishes. In the spirit of autumn and cooler days, you may notice our shorts are a little longer this month.
Contents
Who Can that be Knockin' at My Door—anonymous intranet users
.NET Final Frontier—using sharp objects with Internet Explorer
Pop Pop Shift Shift—stacking up
Getting Selective—stuffing those select boxes
Who Can That Be Knockin' at My Door?
Dear Web Team:
I have an ASP page on our Intranet which I would like to have users access anonymously. The main entry page for the site forces getting NTLM authentication so the user is authenticated when they open this page.
I have read that the logon details are cached locally and are only discarded when the browser is closed. A new browser does open the page as Anonymous, but I need to pass parameters to the page so I would like to open this page via a link. I have tried spawning a new browser window and closing the first window but the NTLM details are still shown in LOGON_USER.
How can I kill the NTLM authentication and force the page to use Anonymous?
Thanks,
Ray Messinger
The Web Team Replies:
Ray, the secret to cracking this chestnut lies in understanding WININET.dll, the networking component of Microsoft Internet Explorer that sits right below URLMON.dll (URL Monikers) and handles all the nitty-gritty of HTTP, including authentication. WININET stores all its state data (such as cached passwords and session cookies) on a per-process basis. This is why windows created using window.open() share the same WININET data, including session cookies and authentication credentials. (Technically, we refer to all of WININET's data as a session, which is why cookies that only last for the lifetime of the browser are called session cookies.)
Unfortunately, pure script will provide you little solace here. If you're willing to drop an ActiveX® control on your page, then what your heart desires will come to you, as your ActiveX control can launch a new instance of the browser with your desired URL passed in as a command-line argument. While deprecated for most uses, the built-in Shell() function of Visual Basic® will work fine for this, and requires much less code than the verbose ShellExecute() discussed from a Visual Basic angle in Knowledge Base Article Q170918. Just define a public method on your control like so:
Public Sub LaunchBrowser(url as String) Shell "iexplore.exe " & url End Sub
(Those of you who feel the need to take penitential hot showers if you don't write everything in C++ will want to call the CreateProcess() API function instead.) You can then rig up your hyperlinks to call this method when they are clicked:
<A HREF="" ONCLICK="javascript:launchBrowserObj.LaunchBrowser('http://server/url/path');">
A different route involves invalidating the current WININET session with a call to the WININET API function InternetSetOption(). Again, in Visual Basic:
Public Declare Function InternetSetOption Lib "wininet.dll" Alias "InternetSetOptionA" _ (ByVal hInternet As Long, ByVal lOption As Long, ByRef sBuffer As Any, _ ByVal lBufferLength As Long) As Integer ' Douglas Adams fans, take note! Public Const INTERNET_OPTION_END_BROWSER_SESSION = 42 Public Sub EndBrowserSession() ' Passing NULL makes this true for all connections in ' the host process. InternetSetOption vbNull, INTERNET_OPTION_END_BROWSER_SESSION, vbNull, 0 End Sub
As noted, this will also invalidate your session cookies, which may or may not be what you're looking for.
.NET Final Frontier
Dear Web Team:
Can I use .NET objects written in C# within Internet Explorer?
Sincerely,
A Curious Reader
The Web Team Replies:
It's an infectious platform, isn't it? The answer, dear Reader, is that you can create Windows Forms (WinForms) controls in any .NET language and host them in Internet Explorer using a variant OBJECT tag syntax designed specifically for .NET assemblies:
<OBJECT ID="obj1" CLASSID="AssemblyName.dll#Namespace.Name.ControlName"> </OBJECT>
AssemblyName.dll is the name of your assembly as it resides on the Web server and Namespace.Name.ControlName is the full-qualified name of your WinForms control. (For richer documentation, see the .NET Frameworks Documentation topic)
To create a WinForms user control in Visual Studio® .NET, select File/Project/New…, select your language from the left-hand pane, and then select Windows Control Library from the right. This will create an assembly with a default control derived from Systems.Windows.Forms.UserControl. (So named because ActiveX controls were called UserControls in Visual Basic 6.0—make sure not to confuse them with User Controls in ASP.NET, which are custom WebForms controls. Just call them "custom WinForms controls" and we'll all remain happy and sane.) You can add properties and methods to the control, run the control in Internet Explorer, and call your properties and methods from either JScript® or Visual Basic Script Edition (VBScript).
Can you still use PARAM tags with the OBJECT tag to supply run-time initialization data to your control? You betcha! In fact, .NET makes it easier than ever. Just define a public property on your control and use that same name as the Name attribute of your PARAM tag. Here's a simple C# property definition:
public string XmlConfigData {
get {
return(xmlUrl);
} set {
xmlUrl = value.ToString();
}
}
And its corresponding PARAM tag:
<OBJECT ID="obj1" CLASSID="AssemblyName.dll#Namespace.Name.ControlName"> <PARAM Name="XmlConfigData" Value=http://servername/configData.xml /> </OBJECT>
The Internet Explorer Runtime host (IEHost.dll) will use Reflection to examine the properties on your control and will automatically do a property set if it finds a match.
This usage of the OBJECT tag has several limitations. You can't use it to load objects stored in the local machine's Global Assembly Cache (GAC). (Your control, however, can use any object in the GAC it wishes.) You can use this syntax with CAB files, but as of Internet Explorer 6.0, you can only have one assembly per CAB file. Finally, your assembly must be downloaded through HTTP or HTTPS. You can't load assemblies from the file system.
There are limitations with scripting WinForms objects—namely, you can only call methods and properties on the top-level object. The Runtime creates a COM-Callable Wrapper for your control so that it looks like a normal COM object to script, but doesn't do the same thing for any Object types returned from your control. Depending on what your control does, this might require an aggressive plan of encapsulation on your part.
You also need to consider your target audience for WinForms controls. End users must have the .NET Runtime installed on their machines. Unlike the Java Virtual Machine download, which weighs in at about 5 megabytes (MB), the .NET Runtime redistribution—a whopping 20 MB—will not auto-install. Because of this, WinForms controls will probably be deployed more quickly in enterprise intranet settings than on the Internet. Fortunately, Internet Explorer modifies its User-Agent string to indicate the version of the Common Language Runtime (CLR) installed on the client machine, so early adopters can fall back to DHTML or ActiveX content if their users can't support .NET. Those using ASP.NET can get at this value using the HTTPRequest.Browser.ClrVersion property defined in System.Web.
Given these difficulties, why would you host WinForms controls in Internet Explorer, beyond the coolness factor? Despite the problems, WinForms controls are superior to their ActiveX counterparts in numerous ways. Since most of their functionality comes from the .NET Framework, they're light and easy to download. The richness of the Framework means no more scrounging for components to reuse, as was typical in Visual Basic and (even more so) C++ ActiveX controls. For those worried about security (and who isn't?), controls operate under the new .NET security model, which allows users and site administrators to set permissions at a high level of granularity. By default, WinForms controls in Internet Explorer operate inside a lockbox that only allows them to connect back to their host machine and write to a specific, protected area of the file system, among other permissions. If you want your control to access the user's file system, either the user (on the Internet) or a site administrator (on an intranet) will need to grant this higher level of trust to your control. While some might see this as restrictive, it's a powerful model, and it will eventually wipe out many of the severe ActiveX security breaches that have plagued us in the past.
The topic of WinForms controls in Internet Explorer is rich and deep, and we've only scratched the surface, particularly as regards security. Our teams are working feverishly to crank out documentation (just look at these hands, will you?!), so stay tuned to MSDN for more.
Pop Pop, Shift Shift…
Dear Web Team:
I translated some code for simulating a Reverse Polish Notation (RPM) calculator from Perl into Jscript. However, the code uses the array methods push(), pop(), shift() and unshift(), and I need to target versions of Internet Explorer that lack these methods. How do I do it?
Sincerely,
Logan Kearsley
The Web Team Replies:
Ah, Perl, once proclaimed "the Duct Tape of the Web." Seriously, Perl's a neat language and its array primitives really help simplify life. Push() and pop() insert and remove elements from the beginning of an array, so we can treat an array as a stack, governed by the rule of FIFO (First-In, First-Out). Shift() and unshift() do the same thing to the end of an array, so we can treat an array as a queue, governed by the rule of LIFO (Last-In, First-Out).
As you probably saw from the JScript Version Information page, Logan, these operators are only supported in Internet Explorer5.5 or greater. (Another convenient method available in 5.5 is splice(), which removes 1..n elements from anywhere in the array. Pop() and unshift() are convenience methods, and you could implement both using splice().)
The best solution is to roll your own JScript stack and queue objects. Since all you need for an RPN calculator is a stack, that's all we define below. But this definition can be used as the template for a version-independent queue as well (just rename the methods and reverse the actions).
// A version-independent stack object prototype for JScript.
// We use an index into the array to increase efficiency instead
// of making expensive calls to slice(); old
// values will just be blown away on subsequent push() operations.
function Stack() {
// Private data
var stackPtr;
var stackArr;
// Methods
this.Push = internal_push;
this.Pop = internal_pop;
this.Clear = internal_clear;
this.Length = internal_length;
// Init this instance.
internal_clear();
}
// Push an element onto the top (end) of the stack.
// @Precondition: elem is not null
// @Postcondition: stack.length == stack.length + 1
function internal_push(elem) {
stackArr[++stackPtr] = elem;
}
// Retrieve an element off of the top (end) of the stack.
// @Precondition: stack is not null
// @Postcondition Old Stack.Length() == StackLength() + 1
function internal_pop() {
if (stackPtr >= 0) {
return(stackArr[stackPtr--]);
} else {
throw("Cannot pop off of an empty stack.");
}
}
// Clear the stack and start anew.
function internal_clear() {
stackPtr = -1;
stackArr = new Array();
}
// Obtain the current stack length. Since we use an index into the array,
// Length() != internalArray.Length.
function internal_length() {
return(stackPtr + 1);
}
Getting Selective
Dear Web Team:
Hi, I find the columns really helpful.
I am a developer for a company that is trying to put together a 3rd generation web site that is only Internet Explorer 5+ using all the latest bits and pieces of coding technology (before [.NET] makes it all obsolete :))
One of the technologies that we make extensive use of is SQL XML templates.
All the data processing is done client side for the sake of speed and the complexity of the options available on the fine grained search page.
But we find our selves suffering from bad performance when using client side XMLDOM to file linked select boxes. We have 8 of these in a chain and most of them in one combination or another are interdependent.
Can you suggest a way to improve performance?
I have included a code snippet of how we build the select boxes.
Public Function bindXML
' find xpath match for attribute
Set objXmlNodelist=xmlDom.selectNodes(varXpath)
'get length of list for loop
totalnodes=objXmlNodelist.length
Set objNode=objXmlNodelist.nextNode
'set selectbox option count to 0
objControl.options.length = 0
i = 0
For t=0 to totalnodes-1
'increase select box options by 1
objControl.options.length =objControl.options.length + 1
' add option details
objControl.options(i).id = objNode.getAttribute(varIndex)
objControl.options(i).value = objNode.getAttribute(varValue)
objControl.options(i).innerText = objNode.getAttribute(varText)
i = i + 1
'move down the node tree
Set objNode=objXmlNodelist.nextNode
Next
End Function
Thanks,
D.conner
The Web Team Replies:
Buttering us up at the start of your letter is a sure fire way to catch our attention! From the description of your performance issues, we were guessing that you were adding quite a few new options to your select boxes. Starting with Internet Explorer 5, the options in a select box are added to the document tree. This means that all options can be accessed through the document.all() collection. If you add a large number of options one by one, each option is inserted into the DOM for your page. We discovered that if you store all of the html for new options into a string, and then add it all at once using outerHTML, it goes much faster. That is, until your strings start getting so long that the creation of the string starts causing performance issues.
In our example below, we add 8000 new options. To get around the performance issues of the string length, we store the code for 100 options at a time and then save that into an array. When we are ready to add the options, we just use a join on the array to turn all of our little strings into one long one.
As a contrast, we also use the createElement method to create new options. This is the method recommended in the DHTML online reference in the MSDN Online library. When adding a relatively small number of options at a time, this method is a little simpler. Next, we added some timer code to both versions so you can see for yourself how fast each method performs. We did find the createElement method took half the time to process as the code you sent us. Even so, it took more than two minutes to fill in all 8000 options. Using the outerHTML and string approach, that same number of options took less than a second!
Using the outerHTML trick does have one drawback—it causes the selectbox to disappear for the split second before the newly rendered one appears. We tried to replace the innerHTML of the select box, but in our version of Internet Explorer, the initial bit of the string was disappearing and making the HTML invalid and resulting in an empty select box. If the flashing of the select box is an issue for you, test using the innerHTML in your own environment, just in case.
Here is the code we were fiddling with when working on this little gem.
<HTML>
<HEAD>
<SCRIPT LANGUAGE="vbscript">
sub rewriteBox
Dim optStrings(100)
startTime = timer()
'generate the html strings, save up to 100 in each array element
For i=0 to 8000
optStr = "<option value=""opt" & i & """>Option " & i
optStrings(i \ 100) = optStrings(i \ 100) & optStr
Next
S1.outerhtml="<select id=""S1"">" & join(optStrings) & "</SELECT>"
ProcessTime.value = timer - startTime
end sub
sub fillBox
startTime = timer()
for i=0 to 8000
set myOpt = document.CreateElement("OPTION")
myOpt.value = "opt" & i
myOpt.text = "option " & i
S2.add(myOpt)
next
ProcessTime.value = timer - startTime
end sub
</SCRIPT>
<STYLE>BODY {font-family:arial;font-size:10pt}</STYLE>
</HEAD>
<BODY>
<BUTTON ONCLICK="rewriteBox">replace select</BUTTON>
<SELECT ID="S1"><OPTION>placeholder</OPTION></SELECT>
<BR />
<BR />
<BUTTON ONCLICK="fillBox">add in options</BUTTON>
<SELECT ID="S2"><OPTION>placeholder</OPTION></SELECT>
<BR />
<BR />
seconds to process:
<INPUT ID="ProcessTime" VALUE="0" />
</BODY></HTML>
Web Team in Short
Encoding URLs, Revisited
Q: Craig has more questions about the difference between the escape() and encodeURI() sets of functions provided by Microsoft Windows Scripting.
- What versions of Microsoft Internet Explorer support the newer, preferred functions?
- Are you saying that escape() should never be used for URL encoding?
- Is there ever an appropriate use for escape(), or is it altogether deprecated?
A: These questions continue from a recent Web Team Talking article.
- The newer set of functions, which includes encodeURI(), are provided with Microsoft JScript, version 5.5, and later. Microsoft Internet Explorer, version 5.5, installs Windows Script, version 5.5. You can download the latest version of Windows Script at the Microsoft Scripting Technologies Web site.
- The escape() function was designed to convert non-ASCII and punctuation characters into a portable format that can be transmitted across the Internet. It is recommended that you use the encodeURI() function whenever possible because it was designed to encode URLs.
- The escape() function is used when you need to send binary information across the Internet or if there is a restriction on the use of punctuation characters, such as in a cookie value.
What's Our Vector, Victor?
Q: Marcel has noticed that Vector Markup Language (VML) lines are anti-aliased in Microsoft Internet Explorer 6, and wants to know how to turn off this behavior.
A: Anti-aliasing is a technique that smoothes the edges of lines to produce a clean image. You can see the effect of anti-aliasing on a diagonal line in the sample HTML listed below—the black line. The default behavior for Internet Explorer 6 is to turn on anti-aliasing for VML elements. If you need to position VML elements with pixel accuracy, you can explicitly turn off anti-aliasing by setting the antialias CSS attribute to false.
<HTML xmlns:v="urn:schemas-microsoft-com:vml">
<HEAD>
<STYLE>
v\:* {behavior:url(#default#VML);}
</STYLE>
</HEAD>
<BODY>
<V:LINE STYLE='antialias:false' FROM="163pt,127pt" TO="252pt,227pt"
STROKEWEIGHT="2.5pt" STROKECOLOR="red"/>
<V:LINE STYLE='antialias:true' FROM="252pt,127pt" TO="352pt,227pt"
STROKEWEIGHT="2.5pt"/>
</BODY>
</HTML>
Calling Stored Procedures
Q: David is encountering an error (800a0bb9 - "Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.") when using ADODB to execute a stored procedure. The error occurs when setting the CommandType property. Here is a section of the VBScript code in his Active Server Page (ASP):
…
SET CMD = SERVER.CREATEOBJECT("ADODB.Command")
…
CMD.CommandText = "{?=call sp_password(?, ?)}"
CMD.CommandType = adCmdStoredProc
A: This error is occurring because you are using the expanded form of a stored procedure call as the CommandText property. When specifying a CommandType property other than adCmdUnknown, the CommandText property must contain a query that matches the command type. Either set the CommandText property to the stored procedure name, as shown below, or set the CommandType property to adCmdUnknown (default) or adCmdText.
… CMD.CommandText = "sp_password"
For more information, please see the Knowledge Base article HOWTO: Call SQL Server Stored Procedures from ASP.
ReadyState, Go
Q: Jon is seeing some unusual behavior with the readyState property of an XML data island. The following code loads an XML file that is generated by an ASP page. There are two problems, the first being that the ReadyState property returns a numerical value (the documentation says this is a string), and the second is that Internet Explorer displays an error dialog "Internet Explorer cannot open the Internet site http://localhost/xmltest.htm. Operation aborted."
<HTML>
<BODY>
<P ID=pBreak>
</BODY>
<SCRIPT LANGUAGE=VBScript>
Sub Check()
Msg CStr(xmlTest.ReadyState)
If xmlTest.ReadyState = 4 Then Msg "COMPLETE"
End Sub
Sub Msg(Content)
pBreak.InsertAdjacentHTML "AfterEnd", Content & "<BR>"
End Sub
</SCRIPT>
<XML ID=xmlTest SRC="http://localhost/xmldata.asp"
ONREADYSTATECHANGE="Check()"></XML>
</HTML>
A: The first behavior you are seeing here is an interesting anomaly that results from using VBScript. You won't see this behavior if you're using JScript. Why is that? Well, VBScript is not case sensitive. Most of the time this is a nice feature. It doesn't matter if the property is named readyState and you've referred to it as ReadyState, right? Wrong! In this unusual situation, the object is returning a different property, which has a numerical value. One workaround is to refer to the correct property name as follows:
xmlTest.readyState
This will return a string value, such as "loading." Alternatively, use the XML document readyState property, which returns a numerical value:
xmlTest.XMLDocument.readyState
The second behavior is probably due to the way you are using DHTML to modify the contents of the document. Although we don't fully understand the reason behind the error you are seeing, changing your Msg subroutine to do the following will fix the problem:
pBreak.InsertAdjacentHTML "BeforeBegin", Content & "<BR>"
We also found that calling setTimeout to force the document update to occur after the onreadystate event handler has exited resolves the problem. This behavior is probably because you are modifying the document from within the onreadystate event handler.
The Web Team
Mark Davis is a software design engineer on the Internet Explorer SDK team. Mark originates from England and is currently training to climb the major summits in the Northwest.
Heidi Housten works as a Consultant with Microsoft Consulting Services in Sweden after spending some time in Developer Support and MSDN. It is only a rumor that she moved there to escape the drizzle of Seattle; she really went for the traditional crayfish parties in August.
Dan Mohr, an engineer with Microsoft Developer Support's Internet Client team, spends his free minutes recording bands in his basement, programming his Commodore 64, and extolling the virtues of late '70s punk rock.
Jay Allen, a Support Engineer for the Internet Client team in Microsoft Developer Support, longs for the integration of Notepad and Emacs Lisp. What little time is not consumed by his four children is usually spent reading math books, studying Japanese and programming in Haskell.