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
Icon@1"
PS>$regId = [System.Guid]::NewGuid().ToString("B").ToUpper()
PS>$registrar.StartComponentRegistration($regId,$regArray)
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:
http://127.0.0.1:4664/search&s=1ftR7c_hVZKYvuYS-RWnFHk91Z0?q=

So from PowerShell:

PS>(Get-Item "HKCU:\Software\Google\Google Desktop\api").GetValue("search_url")
http://127.0.0.1:4664/search&s=1ftR7c_hVZKYvuYS-RWnFHk91Z0?q=

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
del.icio.us/shanselman     del.icio.us/shanselman ... 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
del.icio.us/shanselman     del.icio.us/shanselman ... 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

...etc...

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

or

.\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. He is 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 SherWeb

Using FFMPEG to squish lots of videos using PowerShell

July 2, '06 Comments [2] Posted in PowerShell | XML | Gaming | Tools
Sponsored By

This post will likely win the award this month for "least useful and most obscure use of two totally unrelated tools to do a task that few folks ever would need to do and fewer would be interested in reading about" but I wanted to save it for myself for reference.

I've got a bunch of old TV shows that are in MPEG2 format that were recorded with BeyondTV from SnapStream. I don't want to toss them, but they are fat and wasting space. I'd like to have them available if I wanted to watch them on my PlayStation Portable, but using PSPVideo9 is tedious for more than a few files. Also, the version of FFMPEG that comes with PSPVideo9 is really old.

You can get a binary copy of FFMPEG (as the project team releases source but not binaries) from this friendly fellow here.

FFmpeg is a collection of free software that can record, convert and stream digital audio and video. It includes libavcodec, a leading audio/video codec library. FFmpeg is developed under Linux, but it can be compiled under most operating systems, including Windows. The project was started by Gerard Lantau, an alterego of Fabrice Bellard, and is now maintained by Michael Niedermayer. Notable is that most FFmpeg developers are part of either the MPlayer, xine or VideoLAN project as well. [Wikipedia]

FFmpeg doesn't like WMV files very much, and they are compressed pretty well already. I wanted to run this ffmpeg command line...

ffmpeg -i 'oldname.*' -f mp4 -r 25 -s 320×240 -b 768 -ar 44000 -ab 112 'newname.mp4'

...on all my files that weren't WMV and convert my files to MP4 video at 320x240 and a decent bit rate.

I used PowerShell to exclude this command line, build the filenames and ran this PowerShell command (pipeline) on my folder:

dir -exclude *.wmv | foreach-object { $newname = $_.Basename + ".mp4"; ffmpeg -i "$_" -f mp4 -r 25 -s 320×240 -b 768 -ar 44000 -ab 112 $newname }

Ran for a few hours and the PlayStation Portable is now happy with my copies of old TV shows that aren't on DVD yet.

NOTE: The System.IO.FileInfo object, returned from PowerShell's 'dir', doesn't have a "BaseName" property, so I've added a custom ScriptProperty to my "My.Types.ps1xml" file:

<Types>
   <Type>
     <Name>System.IO.FileInfo</Name>
      <Members>
         <ScriptProperty>
           <Name>Basename</Name>
           <GetScriptBlock>
            $this.Name.Remove($this.Name.Length - $this.Extension.Length);
           </GetScriptBlock>
         </ScriptProperty>
      </Members>
   </Type>
</Types>

As outlined in this post called "BaseName for FileInfo objects" on the PowerShell team blog.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

Sapphire In Steel - Ruby within Visual Studio 2005

July 2, '06 Comments [0] Posted in PowerShell | Ruby | Watir | Bugs
Sponsored By

Steel_ideWell, apparently I'm the last to notice this one (likely because I've been up to my ears in PowerShell, not Ruby) but "SapphireInSteel" is a Visual Studio 2005 Project add-on that adds Ruby *editing* support to Visual Studio.

This has nothing to do with turning Ruby into IL or making Ruby a .NET Language (although those projects exist.) This is about editing and debugging Ruby within Visual Studio 2005.

If you haven't played with Ruby, take a moment and do this:

This'll be great for everyone who does both Ruby and .NET, but especially for those doing .NET plus Watir development. This is apparently a very actively development project so subscribe to their blog.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

A Very Poor Man's Vonage Web Service using Watir

July 2, '06 Comments [13] Posted in Ruby | Watir
Sponsored By

Folks keep calling after 9pm and waking the baby. We use Vonage, and I really wish they had a feature where I could indicate "do not call" times. Alas, they don't. So, then I wished for a Vonage Web Service. Nope.

So, instead here's a Watir script, recorded with a early version of Michael Kelly and John Hann's port of WatirMaker to Ruby (via "ruby watirmaker.rb > myscript.rb") then edited to taste.

#Enable Instant Forward to Voice Mail
require 'watir'
include Watir

ie = IE.new
ie.goto( 'http://www.vonage.com/' )

ie.text_field( :name, 'username' ).set( 'username' )
ie.text_field( :name, 'password' ).set( 'password' )
ie.button( :name, 'submit' ).click

ie.link(:text,"Features").click

ie.button( :name, 'callForwardingButton' ).click

ie.select_list( :name, 'callForwardingSeconds' ).select( 'Instantly' )
ie.text_field( :name, 'singleAddress' ).set( '15035551234' )

ie.button(:value,'Enable').click

ie.link(:text,"Log Out").click
ie.close

And the disable...

require 'watir'
include Watir

ie = IE.new
ie.goto( 'http://www.vonage.com/' )

ie.text_field( :name, 'username' ).set( 'username')
ie.text_field( :name, 'password' ).set( 'password' )
ie.button( :name, 'submit' ).click

ie.link(:text,"Features").click

ie.button( :name, 'callForwardingButton' ).click

ie.button(:value,'Disable').click

ie.link(:text,"Log Out").click
ie.close

These now run on a schedule in my house using the Windows Scheduler. If you run them like 'ruby enable.rb -b' they run in the background and you'll never see Internet Explorer.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

Capturing Video a Web Camera using WIA - Not Possible?

June 30, '06 Comments [0] Posted in Coding4Fun
Sponsored By

My Coding4Fun article using a $4 webcam would capture single frames using WIA, the WIA Automation Layer and upload them to a blog.

However, I get 3 emails a week asking how to capture video using WIA. I don't think it's possible. WIA supports "TakePicture" but that operation isn't fast enough to get more than a few frames a second, and then even only in JPEG format.

WIA does support Video Overlay though so you can see what you're not capturing, so that's nice. ;)

Seriously, though, WIA isn't the way, it appears that DirectShow likely is. This .NET Library wrapper around the notoriously difficult DirectShow APIs says it supports "capture from video capture devices to WMV files." It appears that this library also exposes that notorious hardness, so take a breath before you dive in. It's hardcore.

Other possibilities are the CAPTURENET Sample that might do the trick. Also Kristoffer Vinther (a master sorter at least) has a library that looks pretty capable.

Aside: Yes, that's a signed 12" Vinyl of Bobby McFerrin's Simple Pleasures album, framed on my wall just below Miles Davis. I used to look for good echos for acappella on campus, but I am nothing compared to my buddy from Vertigo Software (and high school) Matt Hempey and the several acappella albums he has under his belt.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

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