![]() |
|
Spaces home Windows Desktop Search -...ProfileFriendsBlogMore ![]() | ![]() |
|
August 02 Adding new search "locations" to Windows Desktop Search...I’ve been hoping that someday I’d actually have a short post to make on my blog about something Windows Desktop Search (WDS) related… But today isn’t that day…
We’ve been hearing some requests that users would like to add new locations to the “All Locations” list in WDS. Maybe something like this:
So that when you type in a query and select this added location you get navigated to a set of web search results like this:
And to be honest the real request we heard was for info about adding new desktop search locations but since we support the adding of both desktop and web search locations to the list I thought I’d unofficially tell you how to add both. As usual this isn’t an officially supported feature and is likely to change in the future. But when it does change we’ll most likely have a UI for adding these things but for now you’ll have to add them manually via the registry. So as a further disclaimer, be comfortable with modifying your registry before making any of these changes. I’d also recommend you make a backup of your registry settings before making any modifications to them.
Adding “MSN Music” to your locations list…
You’ll want to open RegEdit and navigate “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSN Apps\DS\Views\Locations”. Under that key you’ll notice a list of sub-keys for each location in the list:
As you can from the screenshot I’ve already added a new sub-key called Music for the MSN Music location displayed in my “All Locations” list above. To add this same location to your list just add a Music sub-key under Locations and add the values you see in the screenshot. Reboot your machine (or restart Explorer.exe) for the changes to take effect and the next time you launch a WDS results window your “All Locations” list should look just like mine. Well almost like mine. Yours won’t have a separator between “MSN Music” and “Outlook Express”. To get a separator you’ll need to modify the OE sub-key as well:
Change your OE keys Separator value from a 0 to 1. This should add the separator to your list after another reboot.
Understanding the “Locations” registry values…
So now I’ll drill into the details of what these seemingly random registry values mean and how you can use them to build a wide range of ready to use filters. As you’ve noticed we currently support two types of locations, Desktop Search and Web Search. The registry values you need to fill out are largely dependant upon the type of location you want. But there are a few common values used to control the display of the location in the list so let me talk about those first:
As you’ve probably figured out the locations list is built by simply walking the sub-keys of “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSN Apps\DS\Views\Locations” on first use. So to begin with you’ll need to add a uniquely named sub-key to this hive and fill out the common values listed above. If you’re adding a Web Search location you’ll need to fill out the following value as well:
Now anytime you select your new Web Search location we’ll navigate to the locations URL using the terms entered into our query box. For a Desktop Search location things are a little more interesting but follow the same principle as a Web Search location in that you need to tell us how we should filter your query. But for Desktop Search locations we give you 3 different dimensions to filter the query by:
Another thing worth talking about is the fact these locations get stored in HKEY_LOCAL_MACHINE. That means the changes you make to this list of locations will be applied to every user on the machine. But you can avoid that by copying the entire Locations hive to HKEY_CURRENT_USER and making your changes there. That way each user can have their own list of locations if they’d like.
How’s this useful?
So hopefully all that made sense and you have a hundred ideas for new locations you want to create. If so great! Maybe we’ll even turn it into a supported feature in the future. J But if not here are a couple of ideas worth considering:
Anyway… There are a couple of ideas and please let me know if you find this useful or have any comments… Enjoy!
-Steve
UID Values
ItemStore UIDs:
uidStoreAny = 0x30000001 uidStoreFile = 0x30000002 uidStoreMAPI = 0x30000003 uidStoreOE = 0x30000004
TypeFilter UIDs:
uidTypeAny = 0x10000 uidTypeAttachments = 0x80000 uidTypeContact = 0xA0000 uidTypeCommunications = 0x60000 uidTypeEmail = 0x70000 uidTypeCalendar = 0x90000 uidTypeIM = 0x160000 uidTypeTask = 0xB0000 uidTypeDocuments = 0x20000 uidTypeTextDoc = 0x30000 uidTypeNote = 0xC0000 uidTypeSpreadsheet = 0x40000 uidTypePresentation = 0x50000 uidTypeMusic = 0x100000 uidTypePictureVideo = 0xD0000 uidTypePicture = 0xE0000 uidTypeVideo = 0xF0000 uidTypeFolder = 0x110000 uidTypeFavorite = 0x120000 uidTypeProgram = 0x130000 uidTypeMultimedia = 0x140000
July 08 Updated API's released for Windows Desktop Search 2.5!I had actually wanted to blog a few weeks ago the fact we’ve released some new API’s for WDS as part of our 2.5 release. I chose to hold off on posting any comments here because I knew we had fairly significant update in the works. So now that we’ve posted an updated set of files let me tell you about them.
The 2.5 release was largely about internationalization for us but we were able to squeeze in some new API’s which we’ve been hearing requests for. The new API’s provide access to a SearchManager component to simplify setup & management of protocol handlers and a SearchDesktop component to provide programmatic access to our index. Both of these components are implemented as COM objects and require WDS 2.5 or later. The initial SDK we posted contained a single IDL file which required compilation via Visual Studios MIDL utility into a .tlb file before you could do anything useful with it. And if you wanted to use the SDK from within a .NET application you then needed to run the .tlb generated by MIDL through another utility called TLBIMP which generates an interop assembly that can then be added as a reference to your .NET project. Easy right? Well to simplify both your lives and ours (we have to explain this stuff to you) we decided to do this work for you and give it to you in the form of an updated SDK. We also talked Brandon Paddock into putting together a sample C# app to get you started in using the new API’s. I’m sure Brandon will blog about the sample so I’ll just talk a bit about the files in the SDK and the basic principles behind the API. Be sure and check out the sample as there’s some cool code in there that enables data binding of a queries results to standard WinForms controls. I’m also going to save discussions about the SearchManager component for another post as there’s enough to talk about with the SearchDesktop component.
What’s in the SDK? Good question. Here’s a list of the files:
End user agreement SDK EULA.rtf
COM Interface definitions wdsQuery.idl wdsSetup.idl
Output of MIDL wdsQuery.tlb wdsQuery.h wdsQuery_i.c wdsQuery_p.c wdsSetup.tlb wdsSetup.h wdsSetup_i.c wdsSetup_p.c
Output of TLBIMP WDSQuery.dll WDSSetup.dll
The EULA is mandatory, the .idl files define the COM objects we’re exposing, the files built by MIDL are the files you’ll need if you plan to call the API from C++, and the two .dll’s are the .NET interop assemblies generated after we ran the .tlb’s through TLBIMP. For .NET developers the two .dll’s are the only files you’ll need to make your applications work. And in most cases you’ll only need one or the other.
If you create a new C# project and add references to WDSQuery.dll & WDSSetup.dll, you’ll notice from within the Object Browser that two new namespaces are available to your project:
Microsoft.Windows.DesktopSearch.Query Microsoft.Windows.DesktopSearch.Setup
If you browse into those namespaces you’ll notice a SearchDesktopClass class along with a bunch of ADO stuff in the Microsoft.Windows.DesktopSearch.Query namespace and a SearchManagerClass class in the Microsoft.Windows.DesktopSearch.Setup namespace. The two classes are interop wrappers around the two COM components we’ve exposed via the SDK. Like I said above the SearchManager component is only relevant to building new protocol handlers so we’ll save that discussion for another time.
Tell me about the classy SearchDesktopClass class… Ok so now I’m just getting corny… The SearchDesktopClass is the class you’ll use to create a connection and issue a query to the indexer. If you look at the methods exposed by this class you’ll see it’s pretty simple:
Just two methods of which you’re probably only ever going to call one, ExecuteQuery(). Why so simple? It’s a really low level wrapper around the core component used by the UI when it calls the indexer. The UI and the SearchDesktopClass both communicate with the indexer via an OLE-DB Provider exposed off the indexer. More accurately they both communicate with the indexers OLE-DB provider via ADO so you can think of the SearchDesktopClass as a thin wrapper around ADO calls to the indexer which is why results come back as an ADO recordset.
Such low level access to the indexer has PRO’s and CON’s. The PRO’s are that it’s fast and you can pretty much ask for anything you want. The CON is that since you can ask for pretty much anything you want you have to first know what it is that you want. :) Which leads into my explanation of the differences between ExecuteQuery() and ExecuteSQLQuery(). Let’s start with ExecuteSQLQuery().
Calling SearchDesktopClass.ExecuteSQLQuery() The ExecuteSQLQuery() method is pretty much a direct call to the indexers OLE-DB provider via ADO. The only thing we’ve done is hidden some connection setup details from you. It wants as an argument the complete SQL statement that should be evaluated by the indexer. Here’s the SQL generated when you query for “test query” and don’t filter to a specific type:
Here’s another example of the same query, “test query” but filtered to only show e-mails:
These guys look a little overwhelming but they’re not too bad once you understand them. If you look closely at these two expressions you’ll notice two differences; 1) some of the columns are different because we’ve figured out you’re querying for e-mails and we might want to show those specialized columns to you. And 2) The WHERE clause has changed to accommodate the added constraint need to only return e-mail’s.
So how do you know what to put in your SQL expression? Well for the columns you just have to know which ones you want. We don’t have them documented yet because they’re not stable enough to say “We’re going to support these forever!” But check out the sample app and the examples above for a good starter list and I’ll also try to post some additional sample SQL queries in the near future that call out additional columns. Just be aware that some of them most likely WILL CHANGE in the future. At any rate the columns is a fairly manageable portion of the expression but the WHERE clause is another story.
The sample WHERE clauses above are relatively tame because the original query text didn’t contain any Advanced Query Syntax (AQS). Once you start adding AQS to the mix your WHERE clauses can get messy in a hurry. To help you out of this jam we added the ExecuteQuery() method. Remember how I said that’s the only method you’ll ever want to call. :)
Calling SearchDesktopClass.ExecuteQuery() The ExecuteQuery() method implements the proper logic for building a well formed SQL expression before calling the indexers OLE-DB provider via ADO. ExecuteQuery() takes the following arguments:
As you can see, calling ExecuteQuery() is a lot simpler then calling ExecuteSQLQuery() even though it takes more arguments. The only thing special you still need to know is the list of columns you want returned. But watch this and other blogs for updates to the unofficial list of columns currently supported in 2.5.
Final tips…
In summary we can’t wait to see what cool tools you guys build using the new API’s. Check out the sample and use that as a starting point. And here are a couple of final tips for now:
-Steve June 12 Customizing the Stock PreviewsOk so I thought I’d get the ball rolling by talking about how you can customize the item previews displayed in the large search results UI. This is a big topic which isn’t easily covered by a single post so I thought I’d first talk broadly about how the previews work and how to perform basic modifications. In future posts I can drill into more details if there’s interest. But before any of that I need to throw out a disclaimer: Modifying the previews shipped with Windows Desktop Search is not a supported feature. Any new previews you build will not be guaranteed to work in future versions of the product. The documentation that follows should be considered “unofficial” at best. Preview Selection The first topic I should touch on is how the appropriate preview gets chosen when a user selects an item. When a new item is selected, the first thing the preview pane does is it identifies what type of previewer it’s going to display. It currently only has two choices, the folder previewer or the content previewer. The folder previewer is chosen if the item is a file system folder and is simply a shell view over the contents of the folder. All other items get mapped to the content previewer which is an embedded WebBrowser control. At this point we’re only interested in items mapped to the content previewer because they’re the only ones we can modify. Once an item has been mapped to the content previewer it then goes through a second round of analysis to see what type of content should actually be displayed for the item. This decision process is driven by the registry which means you can modify the registry to influence most of the preview selection decisions made. Open RegEdit and look for the key: HKEY_CURRENT_USER\Software\Microsoft\RSSearch\ContentIndexCommon\Previewers You’ll notice a few values and a set of 3 sub-keys Extension, PerceivedType, and Default. Let’s start with the sub-keys. The contents of those 3 keys are used to determine what preview an item gets and I’ve listed them in the order in which they get consulted. The previewer first gets the file extension of the item being previewed (if any) and tries to find a sub-key under the Extensions key. If a sub-key is found preview selection stops and the values of the found key are used to drive preview generation. If no extension key is found then selection moves on to type based selection. Every item in the index has, or at least should have, a value called its perceived type. The perceived type of an item is the value used to tie it to the type filters displayed within the UI and it’s also the value used to tie the item a type wide preview. So for type based selection we simply get the items perceived type and look for a sub-key of the same name under the PerceivedType key. Once found preview selection stops and the values of the found key are used to drive preview generation. If the item has no perceived type or the perceived type isn’t found then the value of the Default key is chosen and selection stops anyway. Preview Generation So the items been mapped to a key within the registry but what do the values mean? We’ll those values bind the previewer to a COM based plug-in that knows how to generate preview content for the item. The content generated by the plug-in can be essentially anything render-able by IE. We currently ship with 3 preview plug-ins and the COM interface they implement isn’t public so you can’t add any new ones. So why are we talking about customizing the previews then? Because one of those plug-ins is pretty special… If you were to look through all of the previewer keys you’d not two recurring patterns. The keys either contain a single default value, one of two GUID’s. Or they contain no default value but several named values. The keys with one of the two GUID’s map the items preview to either the Native (“{015CA7C6-DECD-40dc-AAAC-73EA9940E0F9}”) or Office (“{7A35A3A8-3DEA-40e5-B2AA-21DEF91A219A}”) Previewer plug-ins. Of those two the Native Previewer is the most interesting because it acts as a pass through for the items URL. When an item is bound to this preview we simply navigate our embedded WebBrowser to the URL of the item which is useful for items that IE already does a good job of rendering like PDFs. Even more interesting are the keys without a GUID because they get mapped to a special internal previewer called the Registry Based Previewer. The Registry Based Previewer turns preview creation into an authoring process by generating preview content on the fly from HTML templates containing a set of ASP’ish (read NOT ASP) macros. The remaining bulk of this post will talk about the structure of these templates but first let me break down the registry entries that control what template is used and how it’s rendered. Looking at: HKEY_CURRENT_USER\Software\Microsoft\RSSearch\ContentIndexCommon\Previewers\PerceivedType\contact We see the values:
The blank value for (Default) is what maps this items preview to the Registry Based Previewer. The ContentType value controls the type of content generated by the previewer and can be either 0 for HTML or 1 for MSHTML. I’d recommend you always use 1 as it’s the most flexible and most tested. The ScriptOK value is used to override one of the security decisions we make based upon what store the item we’re previewing lives in. If the item resides on your hard-drive it’s partially trusted so we let script execute by default. But if the item lives anywhere else like say Outlook or OE we treat it as un-trusted and disable script execution by default. The ScriptOK value lets us re-enable script execution on a per type basis. Security is deep topic worthy of its own post but I should also point out at this time that we NEVER let ActiveX controls execute and there’s currently no way to override that decision. Finally, the TemplateUrl value specifies the actual template to use when generating the preview content. As you can see, all of our templates get pulled from a resource dll but you can change these values to point to a file on the users hard-drive which is the key to replacing the stock previews with custom ones! Creating new Preview Templates Probably the easiest way to figure out how to customize the previews is to dive in and start changing them. You’ll notice that I’ve posted several listing which should act as a good starting point for your adventures. Before you download and install any of the listings you need to be very sure that this is something you want to explore. You’ll need to be comfortable working with HTML and modifying the registry. Executing the UseDebugPreviews.reg command will replace all of you’re type based previews (meaning most of them) with a new Debug.htm preview that dumps out most of the available properties within a given view. I’ve also provided a UseStockPreviews.rgs command which should roll everything back to stock but I can’t provide any warranties for any of this so USE AT YOUR OWN RISK! After you’ve downloaded all 3 of the listings you’ll want to run the UseDebugPreviews.reg command to update the registry to use Debug.htm as the preview template for all type wide previews. There’s an assumption made in UseDebugPreviews.reg that Debug.htm is located in the root of your C-Drive so you’ll need to modify that file if that’s not the case. It’s very important that Debug.htm is saved as a UTF-8 encoded file. The macro replacement done by the Registry Based Previewer will not work if you save the file as Unicode which is the default for Notepad.exe. You’ll also need to either reboot your machine or kill/restart Explorer.exe to get the new previews to take effect. It’s worth noting at this point that changes made to an already mapped preview template do not require a reboot. In fact you can modify your template and simply select another item from the results list to see your changes take effect which is nice. So once you have Debug.htm installed you should try some queries out and notice what properties are available to your previews in what views. I said above that Debug.htm will show you MOST of the properties available because some of the views like contacts have a ton of properties. There are around 200 total but not all of the properties for an item are available in every view. For instance when an item is displayed in the Contacts view it has a rich set of properties available. But that same item displayed in the Communications or Everything view has only a subset of its total properties available. The Debug.htm preview displays the availability of the properties that can be shown for an item no matter what view its in and is therefore quite useful. Another thing you might notice while browsing around is that image previews still get mapped to the stock image previewer. That’s because that mapping is done on a per extension basis and UseDebugPreviews.reg only updates the type wide previews. To map an extension say .JPG you’ll need to modify the registry by hand. Change the TemplateUrl value of HKEY_CURRENT_USER\Software\Microsoft\RSSearch\ContentIndexCommon\Previewers\Extension\.jpg to “c:\debug.htm” and re-boot. The previews of .JPG items should now map to the debug preview.
Understanding Preview Templates Without getting into too much depth I’ll give you an overview of the templates and the macros we currently support. The templates themselves are just UTF-8 encoded HTML files with some special macros added in the form of <%cmd:property%>. At preview generation time the template along with the meta data of the currently selected item gets run through a transform engine to generate a valid HTML file for rendering within the Content Previewers embedded WebBrowser control. Most of the macros are about inserting the dynamic values of the currently selected item but there are a few control commands as well. Here’s a quick list of the major (but not all) commands:
There are a few other interesting commands but I won’t dive into them at this point. This is more then enough to get you started. Just use Debug.htm as a starting point and be sure to send me a link to any interesting previews you come up with. Important Notes and Tips
Listing 1: Debug.htmTo create the Debug.htm file copy everything below the “—copy—“ line into an empty Notepad.exe document. Then save the document AS UTF-8 to “c:\Debug.htm”. The saved file MUST be UTF-8 encoded. Use Notepads Save As feature to do this because the default for Notepad is to save as Unicode which won’t work.
—copy— <html> <head> <!-- Template: debug.htm --> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <style> .debug-group { font-family: tahoma; font-size: 12pt; border: 1px solid #7EB6F0; background-color: #2765AB; color: #FFFFFF; } .debug-row-header { font-family: tahoma; font-size: 10pt;background-color: #FFFFFF; color: green; } .debug-row-hidden { font-family: tahoma; font-size: 8pt; background-color: #EEF4FB; color: black; } .debug-row { font-family: tahoma; font-size: 8pt; background-color: #FFFFFF; color: black; } .debug-cell-header { border-bottom: 1px solid #7EB6F0; } .debug-cell { border-bottom: 1px solid #7EB6F0; } </style> <script>
function AddHiddenRow( prop, title, value ) { document.write('<tr class="debug-row-hidden">'); document.write('<td class="debug-cell">' + prop + '</td>'); document.write('<td class="debug-cell">' + title + ' </td>'); document.write('<td class="debug-cell">' + value + ' </td>'); document.write('</tr>'); }
function AddRow( prop, title, value ) { document.write('<tr class="debug-row">'); document.write('<td class="debug-cell">' + prop + '</td>'); document.write('<td class="debug-cell">' + title + ' </td>'); document.write('<td class="debug-cell">' + value + ' </td>'); document.write('</tr>'); }
</script> </head> <body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"> <!-- First we'll dump out the available properties --> <br> <div align="center" style="width: 100%; padding: 0px 15px 0px 15px;"> <div class="debug-group" align="left" style="width: 100%; height=100%;"> <div style="padding: 5px 5px 5px 5px;">Item properties</div> <div style="overflow: auto; width: 100%;"> <table border="0" width="100%" cellspacing="0" cellpadding="5"> <tr class="debug-row-header"> <td class="debug-cell-header" width="100">Property</td> <td class="debug-cell-header" width="100">Title</td> <td class="debug-cell-header">Value</td> </tr> <script> <%if:DocFormat%> AddHiddenRow('DocFormat', '<%title:DocFormat%>', '<%value:DocFormat%>'); <%endif%> <%if:Url%> AddHiddenRow('Url', '<%title:Url%>', '<%value:Url%>'); <%endif%> <%if:HasAttach%> AddHiddenRow('HasAttach', '<%title:HasAttach%>', '<%value:HasAttach%>'); <%endif%> <%if:PerceivedType%> AddHiddenRow('PerceivedType', '<%title:PerceivedType%>', '<%value:PerceivedType%>'); <%endif%> <%if:IsDeleted%> AddHiddenRow('IsDeleted', '<%title:IsDeleted%>', '<%value:IsDeleted%>'); <%endif%> <%if:WorkID%> AddHiddenRow('WorkID', '<%title:WorkID%>', '<%value:WorkID%>'); <%endif%> <%if:IsAttachment%> AddHiddenRow('IsAttachment', '<%title:IsAttachment%>', '<%value:IsAttachment%>'); <%endif%> <%if:ConversationID%> AddHiddenRow('ConversationID', '<%title:ConversationID%>', '<%value:ConversationID%>'); <%endif%> <%if:FileExt%> AddHiddenRow('FileExt', '<%title:FileExt%>', '<%value:FileExt%>'); <%endif%> <%if:Rank%> AddRow('Rank', '<%title:Rank%>', '<%value:Rank%>'); <%endif%> <%if:DocTitlePrefix%> AddRow('DocTitlePrefix', '<%title:DocTitlePrefix%>', '<%value:DocTitlePrefix%>'); <%endif%> <%if:DocTitle%> AddRow('DocTitle', '<%title:DocTitle%>', '<%value:DocTitle%>'); <%endif%> <%if:DocAuthor%> AddRow('DocAuthor', '<%title:DocAuthor%>', '<%value:DocAuthor%>'); <%endif%> <%if:PrimaryDate%> AddRow('PrimaryDate', '<%title:PrimaryDate%>', '<%value:PrimaryDate%>'); <%endif%> <%if:Size%> AddRow('Size', '<%title:Size%>', '<%value:Size%>'); <%endif%> <%if:FileExtDesc%> AddRow('FileExtDesc', '<%title:FileExtDesc%>', '<%value:FileExtDesc%>'); <%endif%> <%if:DisplayFolder%> AddRow('DisplayFolder', '<%title:DisplayFolder%>', '<%value:DisplayFolder%>'); <%endif%> <%if:FlagText%> AddRow('FlagText', '<%title:FlagText%>', '<%value:FlagText%>'); <%endif%> <%if:IsFlagged%> AddRow('IsFlagged', '<%title:IsFlagged%>', '<%value:IsFlagged%>'); <%endif%> <%if:Create%> AddRow('Create', '<%title:Create%>', '<%value:Create%>'); <%endif%> <%if:DueDate%> AddRow('DueDate', '<%title:DueDate%>', '<%value:DueDate%>'); <%endif%> <%if:Importance%> AddRow('Importance', '<%title:Importance%>', '<%value:Importance%>'); <%endif%> <%if:ToName%> AddRow('ToName', '<%title:ToName%>', '<%value:ToName%>'); <%endif%> <%if:CcName%> AddRow('CcName', '<%title:CcName%>', '<%value:CcName%>'); <%endif%> <%if:AttachmentNames%> AddRow('AttachmentNames', '<%title:AttachmentNames%>', '<%value:AttachmentNames%>'); <%endif%> <%if:DocCompany%> AddRow('DocCompany', '<%title:DocCompany%>', '<%value:DocCompany%>'); <%endif%> <%if:Location%> AddRow('Location', '<%title:Location%>', '<%value:Location%>'); <%endif%> <%if:DocCategory%> AddRow('DocCategory', '<%title:DocCategory%>', '<%value:DocCategory%>'); <%endif%> <%if:DocKeywords%> AddRow('DocKeywords', '<%title:DocKeywords%>', '<%value:DocKeywords%>'); |