Joel and that shutdown menu

by Matt 29. November 2006 09:12

Joel Spolsky had a small rant about how to shutdown Vista. And since it was Joel, it's actually become an issue. Unfortunately, I think Joel should look up the meaning of the word "disingenuous".

The crux of the argument is that you have 15 options of what to do when you leave your computer. And that's too many. Certainly sounds too many when you say it like that. Shame it's not quite so clear cut.

The Start menu has two buttons, lock and power off. Firstly, these buttons will be the most pushed. My mom will use this buttons. She won't bother with the menu, which incidentally, contains 7 different actions. It's this menu that's most controversial. I'll come back to that. Other options are the four laptop fn key combinations. Really. How many people have read their laptop manual enough to know that you can turn off, sleep, hibernate and (I'm guessing) lock using the fn key? These buttons are a terrible idea, but how much would Microsoft get slated if they didn't support them? Closing the lid and using the power button are more physical, intuitive ways of shutting down your machine - they have to be supported.

The "advanced" menu is definitely a poor way of doing advanced UI, but put simply, each outcome is stilll required. And what actually is the alternative? Joel suggests combining lock with switch user. Go on - lock Vista now. Look at the screen. Is it just me, or does that button say Switch User? He also suggests merging sleep and hibernate. Great idea. Should we tell Microsoft? Getting rid of log out is probably not going to go down well with enterprises. Restart replaced with shutdown and then power on? This one might actually have legs. But that wouldn't work on a headless server box that I controlled via remote desktop and wanted to restart.

Personally, if it weren't for the video driver power issues I've got, I'd just be closing the lid and wandering off.

Oh, and the iPod does have an on/off button - hold the play button down. It turns off. When it's turned off, I have to press the select button to turn it back on - any other button doesn't work. And that's more opaque than Vista's lock/off buttons, closing the lid or pressing the power button. I get that Joel's point was that the iPod doesn't need an off button, and generally, it doesn't. But unfortunately for Joel's argument, Apple are giving the user more, advanced choices. (And was he really comparing an operating system with an mp3 player?)

Joel, really. Disingenuous. It's a great word.



Implementing IUrlAccessor

by Matt 27. November 2006 10:35

So, if you tilt your head, squint a little bit and use just a dash of imagination, you can say that ISearchProtocol::CreateAccessor is kind of analagous to CreateFile - it abstracts away the access to what the url refers to just as CreateFile abstracts away the accessing of the file system and the hard disk.

But while CreateFile gives you a handle you can pass in to other API functions, CreateAccessor returns back an instance of the IUrlAccessor interface.

And this is where the whole CreateFile analogy breaks down somewhat. Didn't last long, really, did it? IUrlAccessor is not intended to be an equivalent file system API for a url protocol. Instead, it's about getting access to the any of the url's data that's required for indexing - "file" metadata (size, last modified date), security data and actual "file" contents. (When I'm saying "file" it's really just shorthand for "resource referred to by the url passed to CreateAccessor".)

This simple metadata is available directly from the interface (GetSize, GetLastModified, GetSecurityDescriptor), but getting at the contents is a bit more work.

Obviously, the indexer cannot know about the format of all "files" it's asked to index. Especially when you consider that some files contain just content (such as plain text), some contain only metadata (such as mp3 files) and some contain both (e.g. Word files). We need another layer of abstraction. And that's where IFilter comes in.

The IFilter interface is called by the indexer to retrieve content and metadata from the underlying data source (file/url). The primary purpose of IUrlAccessor is to retrieve an IFilter for the resource represented by the url. So let's take a closer look at IUrlAccessor:

interface IUrlAccessor: IUnknown
    HRESULT AddRequestParameter([in] PROPSPEC *pSpec,
                                [in] PROPVARIANT *pVar);
    HRESULT GetDocFormat([out, length_is(*pdwLength), size_is(dwSize)] WCHAR wszDocFormat[],
                         [in] DWORD dwSize,
                         [out] DWORD *pdwLength);
    HRESULT GetCLSID([out] CLSID *pClsid);
    HRESULT GetHost([out, length_is(*pdwLength), size_is(dwSize)] WCHAR wszHost[],
                    [in] DWORD dwSize,
                    [out] DWORD *pdwLength);
    HRESULT IsDirectory();
    HRESULT GetSize([out] ULONGLONG *pllSize);
    HRESULT GetLastModified([out] FILETIME *pftLastModified);
    HRESULT GetFileName([out, length_is(*pdwLength), size_is(dwSize)] WCHAR wszFileName[],
                        [in] DWORD dwSize,
                        [out] DWORD *pdwLength);
    HRESULT GetSecurityDescriptor([out, size_is(dwSize)] BYTE *pSD,
                                  [in] DWORD dwSize,
                                  [out] DWORD *pdwLength);
    HRESULT GetRedirectedURL([out, length_is(*pdwLength), size_is(dwSize)] WCHAR wszRedirectedURL[],
                             [in] DWORD dwSize,
                             [out] DWORD *pdwLength);
    HRESULT GetSecurityProvider([out] CLSID *pSPClsid);
    HRESULT BindToStream([out] IStream **ppStream);
    HRESULT BindToFilter([out] IFilter **ppFilter);

It's a bit of an odd interface, really - you're actually not expected to implement all of it. Methods that don't make sense for your implemented should return E_NOTIMPL.

There are a number of methods that aren't used - AddRequestParameter, GetHost and GetSecurityProvider. The simple metadata methods are pretty much self explanatory - GetSize, GetLastModified and GetSecurityDescriptor (although this last one will need investigating a bit more closely). The rest are all about getting an IFilter.

When the indexer is indexing the file system, the IFilter is selected based on file extension. When indexing via IUrlAccessor, there are more interesting things to take into account, and IUrlAccessor allows you to customise this simple file extension mapping. Remember that if you don't need this flexibility, you can just return E_NOTIMPL. Also note that the docs don't give an order in which these methods are called - I've listed them here in fairly random order:

  • GetCLSID allows you to return back a class Id that can handle this file type (such as Microsoft Word). I'm guessing this is to do with ActiveDocuments? The main purpose for this is to be able to have a url such as ( actually be a Word file without having to have a .doc extension.
  • GetDocFormat allows you to specify a MIME type that takes precedence over the url's extension.
  • If your url scheme just happens to map UNC accessable files to urls, you can just return the file name here, and it'll get indexed the same as file system files.
  • BindToStream allows you to provide a stream over your data. The indexer can then read the file contents from the stream and either save them to a temporary file and bind an IFilter to that file, or bind the IFilter directly to the stream.
  • If none of those methods suit and you want to take complete control of hooking up the IFilter or if the data represented by your url isn't a normal desktop file format (such as a row in a database, or, as in our case, an RSS item), you can return your own IFilter implementation from BindToFilter.

The final two methods alter how the indexing occurs - GetRedirectedURL and IsDirectory.

GetRedirectedURL allows you to return the actual url that should be used while indexing. In other words, if you have a document at a url that gets redirected, this allows you to tell the indexer that a) it's been redirected, and b) any relative links that your IFilter emits are to be resolved against the redirected url. I don't know if this causes the previously stored url to be updated.

IsDirectory tells the indexer that the current url represents a directory. Surprising that. This means the indexer will treat any emitted child urls as being in this folder. Handy for using the "in:" and "under:" search syntax. (Think of searching in Outlook - "in:Inbox", "in:myfolder", "under:trash").

So that's IUrlAccessor. Doesn't look too tricky. I think the next thing to look at will be returning links from IFilter - this is how we're going to crawl the whole store. Then it'll have to be how to represent the RSS feed store as urls. Hopefully then I'll be able to get at some code - although threading might rear it's ugly head...


Windows Desktop Search

First Vista UAC problem

by Matt 24. November 2006 04:25

I've been using Vista for a good few weeks now, and it's only today that I've hit my first UAC problem - you can't "unblock" a file that's been marked as downloaded if you don't have full permissions to it.

Internet Explorer has a feature called Persistent Zone Identifier. When you download a file from a site, the file saved to the hard disk is marked with the site's security zone (e.g. Internet Zone). When you try and run that program, the shell will notice the identifier and display a warning that you are trying to run a file from an untrusted source. It's a nice little security feature.

XP took it a step further. If you have a zip file with a persistent zone identifier, XP's built in zip tool would pass the identifier on to the files it extracted, so any executables you downloaded in a zip file were still subject to the same zone check. Defence in depth.

Vista appears to trump XP. It not only keeps the id for extracted files, but appears to apply it to any files created by a program that is marked with a zone id!

I downloaded a zip file containing a setup program. I extracted it with the built in zip tool (or maybe I just double clicked the setup program while inside the zip folder view, I forget which) and installed the program. It starts up as you log in, and suddenly I'm greeted by the warning dialog - I'm trying to run software downloaded from the internet.

To stop the dialogs, you can do one of two things; uncheck the tick box on the warning itself, or display the executable file's properties and click "unblock". Neither of which work if you're running with UAC and the files are installed to Program Files, and neither option prompts you for elevation. It silently fails. And you can't elevate explorer itself while browsing the folder, so you're kind of stuffed. And running the file elevated gets the warning prompt before the elevation prompt (it runs in explorer's context, so you've got no hope).

But where there's a will, there's a way. This rather useful runmenu tool allows you to run the following command from an elevated command prompt:

runmenu /verb:properties /wait:window file.exe

and you get the file properties dialog open and can successfully unblock the file. (The "wait" is very important)

Msdn has more details about the Persistent Zone Identifier, but it doesn't mention the little tidbit that the identifier is stored in an NTFS stream called "ZoneIdentifier".



Two steps forward, one step back

by Matt 21. November 2006 16:41

That's Vista RTM installed then. Yes, I've got new sounds, but my display problems have gotten worse. RC2 had problems with resuming from sleep and hibernate. An impressive reboot-free update made things better (and improved my Experience Index to boot!) so I had high hopes for RTM.

Unfortunately, that was not to be. I wasn't connected to the internet at install time, and got saddled with the standard VGA driver (Experience Index of 1.0. Booo!)

Windows Update got me an NVidia driver that supports glass, but has bigger problems with power. After sleep, I get well over a hundred balloon notifications that "Display driver nvlddmkm stopped responding and has successfully recovered." (I think this might be a notification from the event log, which is interesting). Not good.

On the plus side, the display driver restarting doesn't blue screen the machine. And reporting the problems to Microsoft shows off the massively improved Windows Error Reporting, resulting in the message that the "Error is being researched", but there's no fix yet. Looks like I'll just have to download the latest nvidia drivers off the net (I think Vista's got 97.17, and the nvidia site has 96.85).

And the new startup sound? Don't like it - it's one of those corporate identity jingle things that Intel's Pentium popularised. At least it's not as irritating as Philips two-tone jingle.



New Version of SingleUserBlog

by Matt 18. November 2006 17:29

Hey, look at me - I'm dog-fooding! I've just uploaded a new version of SingleUserBlog, the first since before it went open source, way back at the start of September. There have been quite a few changes to the code since then; just check out the list on codeplex.

Some of the changes I made have just been migrating changes I'd already made into the mainstream codebase (hence only just upgrading), but the most exciting changes are new ones.

We've got Windows Live Writer support, including uploading images:

Friendly urls are also very cool - instead of, we get Much nicer.

There's even rudimentary support for attachments.

There's still plenty to do (especially making it easier to install)

The only downside to the upgrade is that the change to friendly urls has changed all the guid/links in the rss feed, so it's duplicated all of my posts in IE7's feed reader. Sharpreader hasn't had a problem with it though. Oh, and I've lost some of my web parts customisation in the sidebars - it'd be nice to get that back.

Still, good stuff, and I'm very pleased. Hopefully, I can keep it up-to-date as more changes come in.



Vista video problems solved. Sort of.

by Matt 10. November 2006 03:59

So, my biggest problem with Vista was losing my display when I put the computer into sleep mode.

But that's ok - I got a new video driver the other day, and it's solved it. I close the lid on my laptop, and when I reopen it, I can still see what I was working on!

Shame about hibernation then. Hibernated the machine yesterday, booted up this morning, black screen. Quick spot of lateral thinking later - close the lid, reopen - and the display is back on again.

Nice workaround, but I think I'll be happier with the RTM bits.



Missing Vista feature #2 - reboots

by Matt 8. November 2006 18:31

I've just run a Windows Update on Vista and it found a new driver for my graphics card. I'm warned that I might need to reboot - no surprise there. Off it goes, downloading in the background. Several minutes later, the screen flickers, goes black and pops back to life. New driver, fully installed, no reboots. That's just plain showing off.

PS. If you're interested, #1 missing feature was the notification API.



Implementing ISearchProtocol

by Matt 8. November 2006 18:02

Now that we've got the protocol handler registered, and we've told WDS the default url to start searching against, we need to implement ISearchProtocol. Fortunately, that's very easy:

STDMETHODIMP Init(TIMEOUT_INFO *pTimeoutInfo, IProtocolHandlerSite *pProtocolHandlerSite,
	PROXY_INFO *pProxyInfo)

	// We don't have anything to initialise, or to cleanup from an unexpected termination
	return S_OK;

STDMETODIMP CreateAccessor(...)

STDMETHODIMP CloseAccessor(IUrlAccessor *pAccessor)

	// We don't want to do anything with this, just let the host close it normally
	// (it calls Release). (This method gives us the chance to pool accessors)
	return S_OK;


	// We don't have anything to clean up
	return S_OK;

Currently, we have nothing to setup in the initialise phase, and similarly nothing for ShutDown to do. One important thing to note is that the ShutDown method might not get called. If the host process crashes (and there is a good chance it will - there are a lot of 3rd party plugins running around here) ShutDown won't get called, so if you create anything persistent in Init, you need to clean it up on the next Init.

On the face of things, CloseAccessor is an odd method. Surely the host would just call Release on the IUrlAccessor to close it down? And indeed it will. This method allows the host to notify you that it's about to throw away the UrlAccessor. It's an opportunity for you to e.g. pool the accessor object (by AddRef-ing it and chucking it on a list). I'm not interested in this, so I just do nothing.

The biggy is of course CreateAccessor. Here's the code I missed out above:

STDMETHODIMP CreateAccessor(LPCWSTR pcwszURL, AUTHENTICATION_INFO *pAuthenticationInfo, 
	INCREMENTAL_ACCESS_INFO *pIncrementalAccessInfo, 
	ITEM_INFO *pItemInfo, IUrlAccessor **ppAccessor)

	CComObject<CUrlAccessor> *pUrlAccessor;
	HRESULT hr=CComObject<CUrlAccessor>::CreateInstance(&pUrlAccessor);
	return hr;

It's a bit naive at the moment, but it starts to show the relationship between ISearchProtocol and IUrlAccessor. In comes a url, and out goes an IUrlAccessor object. Whenever you want to get access to a url, you ask the ISearchProtocol. It's kind of analogous to the win32 CreateFile function - if you want access to a file, call CreateFile, get back a handle to the file. Abstract that away from the file system, and you've got ISearchProtocol->CreateAccessor and IUrlAccessor.

This method takes a couple of interesting parameters. It's got the url and some authentication info that is really only of use if you're doing http stuff. It also includes an ITEM_INFO structure that doesn't look too useful. The important one is the INCREMENTAL_ACCESS_INFO structure. This simply contains a size and a FILETIME of the last time the search gatherer knows it was modified. If it's never been searched, this will be zero. If the item hasn't been modified since this timestamp, CreateAccessor can return PRTH_S_NOT_MODIFIED and the item will not be re-indexed. This allows WDS to use an incremental update scheme - all urls reported by your ISearchProtocol will be accessed to see if they've been updated. It's a bit brute force - think trawling your entire hard disk to see if any files have changed, so WDS also supports change notifications, which I'll get to later in the development, once I've figured out how they work. I also need to find out if the modification time is set automatically by the indexer when the url is filtered, or if I have to do somewhere in my code.

I fully expect the method shown above to get more complex - there's no support for incremental updates, for one thing, but I'm probably also going to have to create a different number of IUrlAccessor objects - one for the root, one for folders and one for feeds. All in good time...

Next up, a bit of a closer look at IUrlAccessor - what data can you get out of it, and how do you use it for indexing?

No code today - I want to look at IUrlAccessor first.


Windows Desktop Search

Still registering the protocol handler

by Matt 7. November 2006 07:39

I've got my vpc image set up with XP, VC++, WDS2.6.5 and the various SDKs. I've got the code from last time, and I can register a COM object via a ProgId to handle the "msfeed:" protocol. But that's only half the story. I need to tell WDS what urls to index - at least where to start.

This is easier said than done. To quote the original MSN Desktop Search documentation:

After the protocol handler is registered, use the AddDefaultUrl method in the ISearchCrawlScopeManager interface to set default crawling rules to include and exclude particular URLs and child URLs

It doesn't mention where ISearchCrawlScopeManager comes from or what object implements it - do I implement it, or does WDS? Can I get to it from another object, or do I have to CoCreateInstance it?

Questions to keep you awake at night. I can put you out of your misery - ISearchCrawlScopeManager gets passed into ISearchProtocolOptions->GetDefaultCrawlScope. Clear as mud, eh?

Given this brick wall, there's only one thing to do - implement something. I'm going to create a COM object that implements ISearchProtocol, as this is the main interface that WDS is expecting. This will be the Sticklebackplastic.MsFeedProtocolHandler.1 object we said would handle "msfeed:" so make sure to ignore these instructions and keep the ProgId! I've given this object a threading model of "Both" for the moment. Easy option. I've also created my own trace category, and used the ATLTRACENOTIMPL macro in each method. That, coupled with ATL_DEBUG_QI should report any activity on that object, including unimplemented interfaces. (I could paste the code here, but it's long and not currently very interesting and this post is going to be long enough without it, so just download it all at the end...)

You're going to need DebugView now - atl will write debug info when one of my unimplemented methods is called, or when an unimplemented interface is requested.

But to get that info, I need the code running, and the only way I can get the code running right now is to register the dll.

What I really want WDS to do is get my request to register the protocol handler, fire up the object via it's ProgId, and ask for an interface, or call an unimplemented method so that I can see what to implement next.

The only problem is a bug in 2.6.5. WDS just sits there, oblivious.

You have to give it a bit of a kick, either by exiting WDS and restarting, or by selecting "Index Now" (which you might have to do a couple of times before it actually does the trick). Now, the protocol handler will get created by WDS, and it will ask for ISearchProtocol. It also asks for ISearchProtocolOptions, which hasn't yet been implemented. (I also get asked for ISearchProtocolThreadContext, which is defined in the Vista Windows SDK. I'm going to ignore that for now.)

Now I can implement my GetDefaultCrawlScope, and call AddDefaultUrl with a default url of "msfeed://localhost/". Going to the search options dialog shows this url with a checkbox as one of the things being searched. Looks like we're off to a good start.

You can add more than one url at a time. I could add "msfeed://localhost/" and "msfeed://localhost/matt/". Only the first would show up in the options dialog (perhaps because matt is a directory under localhost?) Add them in the opposite order, and both show up. Go figure.

Another somewhat annoying thing is that if I unregister the protocol handler (using ISearchManager->RemoveProtocol) it doesn't remove it from the options dialog.

There's more to ISearchProtocolOptions than just GetDefaultCrawlScope, but nothing else is being asked for yet, so I'm going to leave it at that.

Next on the agenda is implementing ISearchProtocol.

You can download the current code here.


Windows Desktop Search

Vista and the Desktop Search protocol handler

by Matt 1. November 2006 17:03

Of course, installing Vista does make developing a WDS protocol handler just a tad more awkward. You see, as I hinted at before, I'd rather build on WDS 2.6.x and Vista comes with 3.0.

The major difference between the two is that 2.x runs in the context of the logged on user, and 3.0 runs as a service. And the msfeed APIs get you the feeds for the currently logged on user. I'd rather solve the Vista-runs-as-a-service problem separately to solving the write-a-working-protocol-handler problem.

Good old virtualisation to the rescue. I'll have a Windows XP image up and running in no time.


Windows Desktop Search | Vista

Month List


Comment RSS