<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Free Associations</title><subtitle type="html">When a codger is a coder.</subtitle><id>http://blogs.msdn.com/freeassociations/atom.xml</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/default.aspx" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/freeassociations/atom.xml" /><generator uri="http://communityserver.org" version="2.1.61025.2">Community Server</generator><updated>2005-05-19T10:15:00Z</updated><entry><title>I'm Famous!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2009/02/14/linkback.aspx" /><id>http://blogs.msdn.com/freeassociations/archive/2009/02/14/linkback.aspx</id><published>2009-02-15T03:58:00Z</published><updated>2009-02-15T03:58:00Z</updated><content type="html">&lt;A href="http://www.microspotting.com/2009/02/zeke" mce_href="http://www.microspotting.com/2009/02/zeke"&gt;http://www.microspotting.com/2009/02/zeke&lt;/A&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9423048" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author></entry><entry><title>IEProcess.h - IE inproc APIs</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2008/11/04/ieprocess-h-ie-inproc-apis.aspx" /><link rel="enclosure" type="text/plain" length="2079" href="http://blogs.msdn.com/freeassociations/attachment/9041900.ashx" /><id>http://blogs.msdn.com/freeassociations/archive/2008/11/04/ieprocess-h-ie-inproc-apis.aspx</id><published>2008-11-05T01:01:00Z</published><updated>2008-11-05T01:01:00Z</updated><content type="html">&lt;P&gt;IE8 beta1 introduced Loosely Coupled IE (LCIE) which gives tab a level of process isolation from the frame: &lt;A href="http://blogs.msdn.com/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx" mce_href="http://blogs.msdn.com/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx"&gt;http://blogs.msdn.com/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;For the most part this works pretty well.&amp;nbsp; But certain binary extensions (activeX objecst, BHOs, etc) that are already deployed have assumptions about window hierarchy and process model.&amp;nbsp; To get this to work the IE team has been diligently testing extensions and finding ways to fix them.&amp;nbsp; For the most part we do this by using the Detours library to redirect Windows APIs to private wrappers.&amp;nbsp; We have been using this technique since we added tabs.&amp;nbsp; For tabs we were just avoiding tabs contending for window modality by calling functions like MessageBox().&amp;nbsp; Our hook would effectively created a queue of modal dialogs avoiding confusing dialogs from hidden tabs, etc.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;But not all problems can’t be fixed by just detouring an existing API this way.&amp;nbsp; A reasonable example is when an application has a custom modal dialog manager.&amp;nbsp; This means that our hooks on DialogBoxParam() and related functions won’t be hit.&amp;nbsp; Other hooking options are complex and fragile so we opted to expose the "right" way for external developers.&amp;nbsp; IEProcess.h exposes functions for helping with this: &lt;A href="http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx" mce_href="http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx"&gt;http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;This API was designed with the idea that many components that are loaded in IE can actually be loaded elsewhere as well.&amp;nbsp; So instead of linking to IE dlls directly, some level of indirection is required.&amp;nbsp; This avoids the cost of loading a DLL when it won’t be used, and allows the IE dev team a little flexibility for how we expose these APIs.&amp;nbsp; First a caller detects whether any IEProcess exports with the following code:&lt;/P&gt;&lt;CODE&gt;&lt;PRE&gt;    HMODULE ieModule = IEProcess:GetProcessModule(); 
    //    if ieModule != NULL then there are exports available
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;P&gt;After retrieving the module, the methods are grouped similar to COM interfaces, but are static, and exist for the lifetime of the process.&amp;nbsp; This is similar to doing GetProcAddress() for a set of functions:&lt;/P&gt;&lt;CODE&gt;&lt;PRE&gt;    static const IETabWindowExports* ieExports = GetTabWindowExports(ieModule);
    //   ieExports contains the methods we need to call
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;P&gt;Now if you look here &lt;A href="http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx" mce_href="http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx"&gt;http://msdn.microsoft.com/en-us/library/cc994365(VS.85).aspx&lt;/A&gt; again, you will see how we implemented our detour of DialogBoxParamW(), and this can be fairly easily replicated to a custom dialog manager.&amp;nbsp; WaitForTabWindow() queues your modal call with the tab; when it returns, its your turn.&amp;nbsp; Then take the lock with AcquireModalDialogLockAndParent(), which will create an appropriate top level window if required for security boundary reasons.&amp;nbsp; Then show the modal dialog, and call ReleaseModalDialogLockAndParent() when done.&amp;nbsp; It’s that easy!&lt;/P&gt;
&lt;P&gt;This is super straightforward if you always run in IE, but looks a little awkward to handle the error conditions if you run outside IE.&amp;nbsp; So I wrote a wrapper class that does pass through logic when running outside of IE.&amp;nbsp; It has an H and CPP file.&amp;nbsp; This is how MSHTML does its IE synchronization for HTML Dialogs, which have a custom dialog manager.&amp;nbsp; The header is below, and the CPP is attached.&lt;/P&gt;&lt;CODE&gt;&lt;PRE&gt;#pragma once

/// &lt;SUMMARY&gt;
/// Helper class that does delay load binding to the IEProcess
/// export tables with fallback logic for easy pass through.
/// &lt;/SUMMARY&gt;
class IEProcessHelper
{
public:

    /// &lt;SUMMARY&gt;
    /// Detect whether per-process exports are enabled for this process.
    /// This does it's binding on demand and the result is cached.
    /// &lt;/SUMMARY&gt;
    static bool IsIEProcess();    

    /// &lt;SUMMARY&gt;
    /// Retrieve the tab window related exports for this process.
    /// If they aren't available it returns a table of fallbacks 
    /// with pass through logic so that this never returns NULL.
    /// This does it's binding on demand and the result is cached.
    /// &lt;/SUMMARY&gt;
    static const IETabWindowExports* TabWindow();

private:
    
    static HRESULT WaitForTabWindowNotImpl(
        __in    bool    allowUnknownThread,  
        __in    HWND    hwndParentProposed,  
        __out   HWND*   phwndParentActual);  

    static HRESULT AcquireModalDialogLockAndParentNotImpl(
        __in        HWND    hwndParentProposed,  
        __out       HWND*   phwndParentActual,
        __deref_out HANDLE* phModalDialogLock);

    static void ReleaseModalDialogLockAndParentNotImpl(
        __in_opt HANDLE hModalDialogLock);

private:
    static const IETabWindowExports _TabWindowExportsNotImpl;
    static const IETabWindowExports* _TabWindowExports;
    static HMODULE _hmoduleIEProcess;
    static bool    _checkedIEProcess;
};
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;P&gt;This simplifies our detour so that the following is the new calling pattern, and doesn’t require any kind of fallback logic.&lt;/P&gt;&lt;CODE&gt;&lt;PRE&gt;INT_PTR WINAPI Detour_DialogBoxParamW(
        HINSTANCE hInstance, LPCWSTR lpTemplateName, 
        HWND hwndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam)
{
    INT_PTR nRes = -1;

    HRESULT hr = IEProcessHelper::TabWindow()-&amp;gt;WaitForTabWindow(
            false, hwndParent, &amp;amp;hwndParent);
    if (SUCCEEDED(hr))
    {
        HANDLE hModalDialogLock;
        hr = IEProcessHelper::TabWindow()-&amp;gt;AcquireModalDialogLockAndParent(
                hwndParent, &amp;amp;hwndParent, &amp;amp;hModalDialogLock);
        if (SUCCEEDED(hr))    
        {
            nRes = Real_DialogBoxParamW(hInstance, lpTemplateName, 
                    hwndParent, lpDialogFunc, dwInitParam);

            IEProcessHelper::TabWindow()-&amp;gt;ReleaseModalDialogLockAndParent(
                    hModalDialogLock);
        }
    }
    
    return nRes;
} 
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;P&gt;Check out MSDN related docs for running in Protected Mode: &lt;A href="http://msdn.microsoft.com/en-us/library/bb250462.aspx" mce_href="http://msdn.microsoft.com/en-us/library/bb250462.aspx"&gt;http://msdn.microsoft.com/en-us/library/bb250462.aspx&lt;/A&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9041900" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author><category term="ie" scheme="http://blogs.msdn.com/freeassociations/archive/tags/ie/default.aspx" /></entry><entry><title>Watch out for sprintf()!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2008/08/15/watch-out-for-sprintf.aspx" /><id>http://blogs.msdn.com/freeassociations/archive/2008/08/15/watch-out-for-sprintf.aspx</id><published>2008-08-15T20:44:00Z</published><updated>2008-08-15T20:44:00Z</updated><content type="html">&lt;P&gt;I was looking at some code the other day that does string concatenation with StringCchPrintf(“%s%s”).&amp;nbsp; I can imagine how this would have near optimal performance, and a hopeful developer could easily assume that in fact it has been tuned for this kind of concatenation.&amp;nbsp; But measuring tells all.&amp;nbsp; Three trivial implementations of concat using strsafe.h:&lt;/P&gt;&lt;CODE&gt;&lt;PRE&gt;void concat_printf(PCWSTR str1, PCWSTR str2, PWSTR out, size_t max)
{
    StringCchPrintfW(out, max, L"%s%s", str1, str2);
}

void concat_copy(PCWSTR str1, PCWSTR str2, PWSTR out, size_t max)
{
    StringCchCopyEx(out, max, str1, &amp;amp;out, &amp;amp;max, 0);
    StringCchCopy(out, max, str2);
}

void concat_cat(PCWSTR str1, PCWSTR str2, PWSTR out, size_t max)
{
    StringCchCopy(out, max, str1);
    StringCchCat(out, max, str2);
}
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;P&gt;Measurements for short concatenating strings (~10 chars) and long strings (~1000 chars).&lt;/P&gt;
&lt;TABLE&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;method &lt;/TD&gt;
&lt;TD&gt;short &lt;/TD&gt;
&lt;TD&gt;long &lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;concat_printf &lt;/TD&gt;
&lt;TD&gt;187 &lt;/TD&gt;
&lt;TD&gt;10530 &lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;concat_copy &lt;/TD&gt;
&lt;TD&gt;15 &lt;/TD&gt;
&lt;TD&gt;952&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;concat_cat &lt;/TD&gt;
&lt;TD&gt;15&lt;/TD&gt;
&lt;TD&gt;1264 &lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;Unsurprisingly concat_copy() is the most efficient, with concat_cat() overhead of a strlen() only noticeable with longer strings.&amp;nbsp; But wow, concat_printf() is shocking order of magnitude slower than either of the others.&amp;nbsp; Taking a peak under the covers explains why:&amp;nbsp; MSVCRT is using a FILE* stream abstraction that does a lot more work for every character copy.&amp;nbsp; This is so the code for printf(), fprintf() and sprintf() can all be shared.&amp;nbsp; Generalization at the cost of performance!&amp;nbsp; &lt;BR&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8870268" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author><category term="psz" scheme="http://blogs.msdn.com/freeassociations/archive/tags/psz/default.aspx" /><category term="prf" scheme="http://blogs.msdn.com/freeassociations/archive/tags/prf/default.aspx" /></entry><entry><title>Performance work is slippery</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2008/08/13/performance-work-is-slippery.aspx" /><id>http://blogs.msdn.com/freeassociations/archive/2008/08/13/performance-work-is-slippery.aspx</id><published>2008-08-13T20:57:00Z</published><updated>2008-08-13T20:57:00Z</updated><content type="html">&lt;P&gt;… like a water snake.&amp;nbsp; Which is why we measure, measure and measure again.&amp;nbsp; Even the most obvious rule of thumb can be wrong.&amp;nbsp; I was optimizing some string parsing code (which is almost always ripe for optimization), and trying to reduce the cost of traversing a string.&amp;nbsp; &lt;/P&gt;When iterating over the elements of an array there are two typical models:&lt;BR&gt;index increment &lt;PRE&gt;    while (ptr[index] != 0)
    {
        index++;
        //  do stuff with ptr[index]
    }
&lt;/PRE&gt;pointer increment &lt;PRE&gt;    while (*ptr != 0)
    {
        ptr++;
        //  do stuff with *ptr
    }
&lt;/PRE&gt;
&lt;P&gt;And while they both generate reasonable code the index increment is typically ~2x the cost of the pointer increment.&amp;nbsp; For iterations that aren’t called much or that have expensive inner operations this is likely not noticeable.&amp;nbsp; This makes sense because it avoids having to recalculate the offset to the current character inside the loop (ptr[index]).&amp;nbsp; The incremented pointer is the current offset.&amp;nbsp; My rule of thumb&amp;nbsp; is to use array indexes when random access is required and use pointer increment for iteration.&lt;/P&gt;
&lt;P&gt;As an example for this rule of thumb, I wrote the simplest of string iterations: calculating a string’s length.&lt;/P&gt;&lt;PRE&gt;size_t slen_index(PCWSTR str)
{
    size_t index = 0;
    while (str[index] != 0)
    {
        index++;
    }
    return index;
}

size_t slen_ptr(PCWSTR str)
{
    PCWSTR ptr = str;
    while (*ptr != 0)
    {
        ptr++;
    }
    return ptr - str;
}
&lt;/PRE&gt;
&lt;P&gt;And then I measured to find out how much faster the ptr loop is (lower numbers are faster):&lt;/P&gt;&lt;PRE&gt;    slen_ptr   = 265
    slen_index = 188
&lt;/PRE&gt;
&lt;P&gt;Surprise, the intuitively “faster” version is actually slower.&amp;nbsp; Peeking at the disassembly provides a quick answer.&amp;nbsp; slen_index() keeps index in a register instead of a stack location, whereas slen_ptr()stores the incremented back into the stack variable.&amp;nbsp; This is a pathological case where the compiler is able to make a better optimization because we don’t do anything with the characters we are traversing. Let's check out functions that actually do something in the loop.&lt;/P&gt;&lt;PRE&gt;#define IS_ALLOWED(ch) (((ch) &amp;gt; 0x20) &amp;amp;&amp;amp; (((ch) &amp;lt; 0x80))) 

bool isallowed_index(PCWSTR str)
{
    size_t index = 0;
    while (str[index] != 0)
    {
        index++;
        if (!IS_ALLOWED(str[index]))
            return false;
    }
    return true;
}

bool isallowed_ptr(PCWSTR str)
{
    PCWSTR ptr = str;
    while (*ptr != 0)
    {
        ptr++;
        if (!IS_ALLOWED(*ptr))
            return false;
    }
    return true;
}
&lt;/PRE&gt;
&lt;P&gt;And now measure (lower numbers are faster): &lt;PRE&gt;    isallowed_ptr    = 250
    isallowed_index  = 390
&lt;/PRE&gt;
&lt;P&gt;That’s more like it!&amp;nbsp; My rule of thumb makes more sense, but the stronger rule is to measure, measure, and measure again!&lt;/P&gt;
&lt;P&gt;PS - these numbers also show that for most functions it probably won't make a difference in your app.&amp;nbsp; But if the function is called a lot, it can (eg strlen()).&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8859924" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author><category term="psz" scheme="http://blogs.msdn.com/freeassociations/archive/tags/psz/default.aspx" /><category term="prf" scheme="http://blogs.msdn.com/freeassociations/archive/tags/prf/default.aspx" /></entry><entry><title>How do explorer context menus sort verbs?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2005/05/19/420208.aspx" /><id>http://blogs.msdn.com/freeassociations/archive/2005/05/19/420208.aspx</id><published>2005-05-20T00:36:00Z</published><updated>2005-05-20T00:36:00Z</updated><content type="html">&lt;P&gt;Windows Explorer has a default implementation for IContextMenu commonly called &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/cdeffoldermenu_create2.asp"&gt;DefCM&lt;/A&gt; that has been used since Win95. It has many clients and has evolved over time, of course, but its primary focus has always been to support the most important of shell folders: File System Folder. A big part of this support revolves around extensibility. &lt;a href="http://blogs.msdn.com/oldnewthing/"&gt;Raymond&lt;/A&gt; has been talking about &lt;a href="http://blogs.msdn.com/oldnewthing/search.aspx?q=IContextMenu&amp;amp;p=1"&gt;hosting IContextMenu&lt;/A&gt;, I am going to talk about how IContextMenu extensions interact with DefCM. And while I may call out the behavior of older versions of Windows to explain things, I am going to focus on the behavior of WinXP. &lt;/P&gt;
&lt;P&gt;The first thing to read is &lt;A href="http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/fileassociations/fa_verbs.asp?frame=true"&gt;Verbs and File Associations&lt;/A&gt; on MSDN. The section on "Static vs. Dynamic Verbs" explains that there are really two extensibility methods. this is important since they will be aggregated together by DefCM.&amp;nbsp; DefCM enumerates verbs from a set of registry hkeys typically generated from file associations. For more details about these keys look &lt;A href="http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/fileassociations/fa_perceived_types.asp?frame=true"&gt;here&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;DefCM starts by gathering all of the IContextMenu extensions which are registered under the "shellex\contextmenuhandlers" subkeys. There is also one implicit object for file associations that handles static verb registration which is added first. The result of this enumeration is an array of objects starting with the static verb implementation, followed by each of the registered ContextMenuHandlers in order of the keys above.&amp;nbsp; Handlers that are registered under two keys are pruned to a single entry.&lt;/P&gt;
&lt;P&gt;In order to get the expected menu order, DefCM calls QueryContextMenu() on each of these objects to insert verbs in reverse order, and has each one insert at the top of the menu (zeroth position). As a result the static verbs are inserted last and end up at the top, which is good since usually these guys are the default.&lt;/P&gt;
&lt;P&gt;So the sorting is based on the following elements in decision order:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Key priority (eg, txtfile, *, AFSO) 
&lt;LI&gt;Registry Enumeration order of shellex\contextmenuhandlers with a special case for static verbs always being first 
&lt;LI&gt;IContextMenu Implementation order &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;So&amp;nbsp;if there is any contention for position, there is no consistent way for an extension to guarantee their relative position within the menu.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=420208" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author><category term="ext" scheme="http://blogs.msdn.com/freeassociations/archive/tags/ext/default.aspx" /></entry><entry><title>The Bizarre and Unhappy Story of 'file:' URLs</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/freeassociations/archive/2005/05/19/the-bizarre-and-unhappy-story-of-file-urls.aspx" /><id>http://blogs.msdn.com/freeassociations/archive/2005/05/19/the-bizarre-and-unhappy-story-of-file-urls.aspx</id><published>2005-05-19T19:15:00Z</published><updated>2005-05-19T19:15:00Z</updated><content type="html">&lt;P&gt;For my first blog entry, I will start with something I wrote for my team back in 1997 (reedited for this blog) that has been helpful to generations of subsequently bewildered developers:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr&gt;
&lt;P&gt;Go back to&amp;nbsp;1995 and a small team dreaming an impossible dream of the&amp;nbsp;Internet on Windows!&amp;nbsp; URLs were young, so young they still hadn't been seen in a television ad....&amp;nbsp; &lt;/P&gt;
&lt;P&gt;IE1.0 was just a fledgling trying to get some features that Mosaic (or the looming competitor) didn't have.&amp;nbsp; Since we shipped with Win95 and a big feature for Win95 was Long File Names, so IE needed to have support for spaces in names.&amp;nbsp; Since there was no spec for the "file" protocol scheme (heck there was practically no spec for any protocol) we added automagical escaping spaces.&amp;nbsp; We happily escaped spaces all over the place, and since nobody used our browser it didnt make a whits difference.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;Now IE began to mature and grew into a precocious 3-year-old, and when the powers that be noticed this, they decided that 3 was such a big number that the browser needed a new image.&amp;nbsp; That image was componentization.&amp;nbsp; The browser was taken and split in three major parts.&amp;nbsp; One part became mshtml (for rendering HTML) another shdocvw (for hosting DocObjects), and the third was wininet and urlmon (two binaries that were basically inseparable for downloading and caching content).&amp;nbsp; Unfortunately a key component from the platform was missing, so it was replicated into each of the other components.&amp;nbsp; That was an URL parsing platform.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;No one component could agree with the others about the best way to handle the wild URLs that were so plentiful.&amp;nbsp; So each component came up with slightly different rules, although very logical, based on its personal fantasies of the one true URL.&amp;nbsp; The fantasies were fairly similar but the devil is in the details and these URLs went through a lot of metamorphoses on each trip down the stack to the wire.&amp;nbsp; Now each breed of URL had its own issues with these transformations, but in the end all children must be pushed from the nest to learn to fly on their own.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Enter IE4, Nashville, Win98, the next generation of everything.&amp;nbsp; And with it the new patron saint of URL parsing.&amp;nbsp; Now someone undertook the plight of the underrepresented URLs very seriously and wished to give them the respect they deserved.&amp;nbsp; Some cried with fear, and others with joy, but none could debate the necessity of a savior.&amp;nbsp; So the little URLs were taken and given a safe place to live and be loved:&amp;nbsp; shlwapi.dll.&amp;nbsp; And the saint went amongst the browser and the browser's friends and said, "Let no one but the chosen ones touch or talk or molest these URLs, and there will be a peace among us."&amp;nbsp; Some resisted, but they were put down as heretics should be and a peace came upon the land.&amp;nbsp; Except for the one abused problem URL:&amp;nbsp; file:.&lt;/P&gt;
&lt;P&gt;No one had been quite as abused as the the little file: URL.&amp;nbsp; This URL was special because we had always used files and DOS paths (and no one at the time knew about path canonicalization attacks), everyone was quite sure what they looked like , acted like, and even tasted like.&amp;nbsp; It didn't help that the file: protocol remained in RFC limbo as a platform/OS specific protocol.&amp;nbsp; So the browser and the browser's little friends would take turns dressing a DOS path like an URL in a pink bunny suit and undressing the URL with a pair of rusty scissors, pretending it was the same DOS path they started with.&amp;nbsp; Only the simplest of URLs was able to withstand this abuse, and it soon became clear that something would have to be done, lest the little file: URLs go off on their own and be lost forever.&lt;/P&gt;
&lt;P&gt;After much praying and meditation and consultation, it was finally decided that file: URLs must have two forms, one being the well formed easily communicable URL form and the other a legacy mutant form of a DOS path.&amp;nbsp; These two forms had different purposes, and soon different personalities.&amp;nbsp; The URL form lived happily inside the browser, but any time the browser needed to talk about the file: URL with its friends, it would clone and mutate the URL into its ugly half DOS form.&lt;/P&gt;
&lt;P&gt;What this MEANS:&lt;/P&gt;
&lt;P&gt;There are two kinds of file: URLs.&amp;nbsp; The first is the well formed URL style.&amp;nbsp; This is allowed query strings, fragment IDs, escape sequences and all the other goodies that URLs are supposed to support.&amp;nbsp; We call this a healthy file: URL.&amp;nbsp; The other kind is basically a DOS path with "file://" stuck on the front.&amp;nbsp; We use this for legacy communication with outsiders only.&amp;nbsp; We call this the legacy file: URL. Examples: &lt;PRE&gt;&lt;FONT size=4&gt;
DOSPATH:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c:\windows\My Documents 100%20\foo.txt &lt;BR&gt;HEALTHY:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;file:///c:/windows/My%20Documents%20100%2520/foo.txt
&lt;BR&gt;LEGACY:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; file://c:\windows\My Documents 100%20\foo.txt &lt;BR&gt;
&lt;BR&gt;DOSPATH:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \\server\share\My Documents 100%20\foo.txt &lt;BR&gt;HEALTHY:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;file://server/share/My%20Documents%20100%2520/foo.txt &lt;BR&gt;LEGACY:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;file://\\server\share\My Documents 100%20\foo.txt &lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;NOW all of the Url* APIs in shlwapi, and thus everyone in IE4, understand the difference between these two types of URLs and act accordingly.&amp;nbsp; as long as a client of the APIs, doesn’t try to access the data directly, there is no issue whatsoever.&amp;nbsp; in other words as long as you dont have code anywhere that looks like this: &lt;PRE&gt;&lt;FONT size=4&gt;LPTSTR GetFilePath(pszUrl)
&lt;BR&gt;{
&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (0 == lstrcmpn("file://", pszUrl, sizeof("file://")))
&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return pszUrl + sizeof("file://");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return NULL;&lt;BR&gt;}&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;but instead always call shlwapi like this:&lt;BR&gt;&lt;PRE&gt;&lt;FONT size=4&gt;PathCreateFromUrl(pszUrl, pszPath, cchPath, dwFlags);&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;You will always be happy and golden.&amp;nbsp; Straying from the path is straying into unknown lands.&amp;nbsp; There are of course flags that you can pass into some of the APIs that will change the form the file: URLs when they come out, but I highly recommend not using it for any reason, as it just increases the chances of creating more bizarre unsupportable behavior.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=420059" width="1" height="1"&gt;</content><author><name>zekel</name><uri>http://blogs.msdn.com/members/zekel.aspx</uri></author><category term="psz" scheme="http://blogs.msdn.com/freeassociations/archive/tags/psz/default.aspx" /><category term="ie" scheme="http://blogs.msdn.com/freeassociations/archive/tags/ie/default.aspx" /></entry></feed>