Scott Hanselman

Searching Google Desktop from PowerShell

July 2, '06 Comments [1] Posted in PowerShell | Javascript | XML
Sponsored By

I just noticed that Sean McLeod created a nice cmdlet for PowerShell that lets you query Windows Desktop Search from the PowerShell Command Line. I used to use Windows Desktop Search but got tired of it hanging and switched to, and settled on, for now, Google Desktop Search and I've been very happy with it.

After reading this I said, I'd like to created a cmdlet for Google Desktop Search, but who has the time? (Maybe I'll do it later)

I figured I could do something quick and dirty though. I took a look at the Google Desktop API Developer's Guide and saw some JavaScript samples for their psycho and obscure COM API. (Google does nearly everything in C++. Even their .NET sample code smells like C++.)

I did this at the PowerShell command line, as a client of the Google Desktop Search has to "register" itself, and I got stuck:

PS>$registrar = new-object -com "GoogleDesktop.Registrar"
PS>$regArray = "Title","Searching GDS from PowerShell","Descripton","For Fun","Icon","My
PS>$regId = [System.Guid]::NewGuid().ToString("B").ToUpper()
Exception calling "StartComponentRegistration" with "2" argument(s): "The component description must contain a SAFEARRAY of six or eight VARIANTs" At line:1 char:38 + $registrar.StartComponentRegistration( <<<< $regId,$regArray)

...and they lost me at SAFEARRAY. I don't know why this little .NET array didn't get marshalled correctly. Probably because it's marshalled as a SAFEARRAY of BSTRs instead. Anyway, too hard, patience waning.

But, hey, GDS runs a local web server, right? So I should be able to query it locally. The docs say:

The search query URL, including your security token, is stored in the registry at:
HKEY_CURRENT_USER\Software\Google\Google Desktop\API\search_url
To use the example above, the stored query URL would be something like:

So from PowerShell:

PS>(Get-Item "HKCU:\Software\Google\Google Desktop\api").GetValue("search_url")

Cool. Then I can add the query after the "q=" and "&format=xml" to get something that PowerShell can sink its teeth into. I'll need to UrlEncode the query.

UPDATE: Note the num= and flags= at the end of the queryString. That indicates that we are only interested in files and we'll take as many as 1000.

PS>$query = "PowerShell"
PS>$searchUrl = (Get-Item "HKCU:\Software\Google\Google Desktop\api").GetValue("search_url")
PS>[System.Reflection.Assembly]::LoadWithPartialName("System.Web") > $null
PS>$newQuery = $searchUrl + [System.Web.HttpUtility]::UrlEncode($query) + "&format=xml&flags=576&num=1000"
PS>$webclient = new-object System.Net.WebClient
PS>$resultsXml = [xml]($webclient.DownloadString($newQuery))
PS>$resultsXml.results.result | select title, snippet, category

title                      snippet                    category
-----                      -------                    --------
C:\Temp\fusionlogs\Defa... NET\Framework\v2.0.5072... file
Windows <b>PowerShell</... Windows <b>PowerShell</... web ... web
C:\Temp\fusionlogs\Defa... NET\Framework\v2.0.5072... file
Windows Desktop Search ... Windows Desktop Search ... web
User Profile: Greg Borota  User Profile: Greg Boro... web ... web
Microsoft Survey            Microsoft Survey Th... web
Discussions in Windows ... Discussions in Windows ... web
Search Results: <b>powe... Search Results: <b>powe... web

Cool. So now I want just the files, and I want them to be actual FileInfo objects.

PS> $resultsXml.results.result | where { $_.category -eq "file"} | foreach-object { get-item $_.url }

    Directory: Microsoft.PowerShell.Core\FileSystem:: C:\Temp\fusionlogs\Default\powershell.exe

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          7/2/2006   3:08 AM       1625 System.Web.HTM
-a---          7/2/2006   2:36 AM       1695 System.Windows.Forms.HTM


Now I'll save the whole file in my path in a file called "search-googledesktop.ps1" and change the first line to "$query = $args" to take command line arguments.

Since the objects that are coming out of this script are real FileInfo objects retrieved with the call to get-item in the final line, I can use my script in a larger pipeline like this:

.\search-googledesktop.ps1 "powershell" | get-content


.\search-googledesktop.ps1 "powershell" | get-location

or delete any file that has my personal information like a social security number in it.

.\search-googledesktop.ps1 "123" | remove-item -whatif

Seriously, once you have an indexer on your system, search for your Social Security Number. You'd be amazed at the old Excel sheets and crap that number gets into.

Anyway, cool. Sure beats their crap COM API.

File Attachment: search-googledesktop.ps1 (474 bytes)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Sunday, July 02, 2006 12:39:23 PM UTC
>I'd like to created a cmdlet for Google Desktop Search, but who has the time?

Ahh, a mere mortal like the rest of us. I feel less ashamed that I can't finish my reading list now! Credibility is building once again!

Have a great holiday with the family!

Thanks Scott!
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.