Get the 48×48 or 256×256 icon of a file on Windows

November 13th, 2008

Getting the 16×16 and 32×32 icons on Windows is relatively easy and is often as simple as one call to ExtractIconEx.

However, getting the extra large (48×48) and jumbo (256×256) icons introduced respectively by XP and Vista is slighly more complex. This is normally done by:

  1. Getting the file information, in particular the icon index, for the given file using SHGetFileInfo
  2. Retrieving the system image list where all the icons are stored
  3. Casting the image list to an IImageList interface and getting the icon from there

Below is the code I’m using in Appetizer to retrieve the extra large icon. If needed it can easily be adapted to get the jumbo icon.

Update: To do the same thing in C#, see the link in the comments below.

#include <shlobj.h>
#include <shlguid.h>
#include <shellapi.h>
#include <commctrl.h>
#include <commoncontrols.h>

// Get the icon index using SHGetFileInfo
SHFILEINFOW sfi = {0};
SHGetFileInfo(filePath, -1, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);

// Retrieve the system image list.
// To get the 48x48 icons, use SHIL_EXTRALARGE
// To get the 256x256 icons (Vista only), use SHIL_JUMBO
HIMAGELIST* imageList;
HRESULT hResult = SHGetImageList(SHIL_EXTRALARGE, IID_IImageList, (void**)&imageList);

if (hResult == S_OK) {
  // Get the icon we need from the list. Note that the HIMAGELIST we retrieved
  // earlier needs to be casted to the IImageList interface before use.
  HICON hIcon;
  hResult = ((IImageList*)imageList)->GetIcon(sfi.iIcon, ILD_TRANSPARENT, &hIcon);

  if (hResult == S_OK) {
    // Do something with the icon here.
    // For example, in wxWidgets:
    wxIcon* icon = new wxIcon();
    icon->SetHICON((WXHICON)hIcon);
    icon->SetSize(48, 48);
  }
}

Appetizer 1.0 is out

November 12th, 2008

I have just released version 1.0 of Appetizer. It is a free application launcher, or dock, for Windows. It allows organizing and launching your applications and other shortcuts into a convenient dock. The app is skinable and entirely customizable. It is currently available in five different languages.

The application also supports the PortableApps USB format. So if you use Appetizer on a USB key in the PortableApps format, the application will detect it and automatically import all your applications along with the ‘Documents’, ‘Videos’, ‘Music’ and ‘Pictures’ folders.

Follow this link to download it.

Appetizer


Dump a SVN repository from a URL

November 7th, 2008

Dumping a repository is sometime necessary, for example when you want to transfer it from one place to the other, say from Google Code to Sourceforge. The dump is normally created using svnadmin. However if you ever try to dump a remote repository, you’ll find out that it can’t be done:

svnadmin dump http://projectname.googlecode.com/svn > repodump

svnadmin: 'http://projectname.googlecode.com/svn' is an URL when it should be a path

One way around that is to use svnsync. This utility allows you to synchronize a local depository with a remote one. So the technique here is to create an empty repository, synchronize it with the remote one, and, finally, dump the local repository.

Here are some notes on how to do that:

1. Create a local repository

svnadmin create c:/repository

2. Add a “pre-revprop-change” hook to the local repository.

echo > c:\repository\hooks\pre-revprop-change.cmd

This is done by opening the “hooks” folder of the repository and adding a blank “pre-revprop-change.cmd” file into it. This will enable revision property change, which is necessary for synchronization.

On Linux and Mac, please follow the instructions in this comment.

3. Synchronize your new repository with the remote one:

svnsync init file:///c:/repository https://example.googlecode.com/svn

svnsync sync file:///c:/repository

Note: if you get this error: “Cannot initialize a repository with content in it“, it means that you’ve made some changes to the repository. In which case, the best thing to do is to just delete it and start over. The repository must be completely empty for the synchronization to work.

4. Finally, once the synchronization is done, simply dump your local repository:

svnadmin dump c:/repository > repodump

If everything went well, repodump should now contain the exact content of the remote repository.

Everything in one batch script

The batch script below allows to do all that in just one script. Simply replace REPO_PATH and REPO_PATH_NUX by the path to your repository and REPO_URL by the repository URL.

SET REPO_PATH=c:\your\local\repository
SET REPO_PATH_NUX=file:///c:/your/local/repository
SET REPO_URL=https://example.com/remote/repository

REM Uncomment the line below if the repository folder already exists
REM rmdir %REPO_PATH% /s/q
mkdir %REPO_PATH%
svnadmin create %REPO_PATH%
echo > %REPO_PATH%\hooks\pre-revprop-change.cmd
svnsync init %REPO_PATH_NUX% %REPO_URL%
svnsync sync %REPO_PATH_NUX%

Get the URL of a YouTube FLV in Flash

October 31st, 2008

YouTube recently changed the way it serves FLV videos and previous methods to get the URL of the FLV files no longer work. As a workaround, it is still possible to retrieve it by:

1. Downloading the YouTube webpage where the video is embedded
2. Parsing it to extract the “t” and “video_id” parameters
3. Building the FLV URL using the old method.

Obviously, having to download and parse the YouTube web page is a lot slower than the previous method, which was just about reading the HTTP headers, but at least it works.

The code snippet below allows to do just that:

public function getFLVURL(iYouTubeVideoURL) {
  // iYouTubeVideoURL is the "watch" URL such as:
  // http://www.youtube.com/watch?v=OVVvAnU3m6E

  var loader:URLLoader = new URLLoader();
  var req:URLRequest = new URLRequest(iYouTubeVideoURL);
  loader.addEventListener(Event.COMPLETE, this.getFLVURL_complete);
  loader.addEventListener(IOErrorEvent.IO_ERROR, this.getFLVURL_ioError);
  loader.load(req);
}

private function getFLVURL_complete(iEvent) {
  var pageData:String = iEvent.currentTarget.data;

  // Get the "t" parameter
  var regex:RegExp = /"t": "(.*?)"/;
  var result:Array = pageData.match(regex);
  if (result.length < 2) {
    trace("Error parsing YouTube page: Couldn't get 't' parameter");
    return;
  }
  var param_t = result[1];

  // Get the "video_id" parameter
  regex = /"video_id": "(.*?)"/;
  result = pageData.match(regex);
  if (result.length < 2) {
    trace("Error parsing YouTube page: Couldn't get 'video_id' parameter");
    return;
  }
  var param_video_id = result[1];

  var flvURL:String = constructFLVURL(param_video_id, param_t);
  trace("Here's the FLV URL: " + flvURL);

  flvTextBox.text = flvURL;
}

private function getFLVURL_ioError(iEvent) {
  var loader = iEvent.target;
  trace("Couldn't download YouTube web page: " + iEvent);
}

private function constructFLVURL (video_id:String, t:String):String {
  var str:String = "http://www.youtube.com/get_video.php?";
  str += "video_id=" + video_id;
  str += "&t=" + t;
  return str;
}

Update (01/12/2008):

As it turns out, this technique only works from Adobe AIR and not from a web page. This is because YouTube doesn’t allow crossdomain calls, which means that if a Flash application tries to fetch one of their pages, it will fail with a security error.

To go around this issue, it is possible to use a simple PHP stub file, which fetches the YouTube page and send it back to Flash. The PHP file would be as follow:

<?php

$url = urldecode($_GET["url"]);
header("Content-Type: text/html");
readfile($url);

?>

Then, in Flash, instead of calling the YouTube URL directly, you call the PHP URL:

var youTubeURL = "http://uk.youtube.com/watch?v=bxfpMGLMZ7Y"
var stubURL = "http://example.com/stub.php?url=" + escape(youTubeURL)
getFLVURL(stubURL);

This small demo illustrates all that. The source code with PHP stub is available here.

Update (19/03/2009):

I’ve posted a follow up to this post with some information on how to get the HD quality version of the videos:
Get the URL of an HD-quality Youtube video.


Create a short unique ID in PHP

October 21st, 2008

Mads Kristensen describes a nice way of generating shorter UUIDs in C#. The idea is to take the 16 bytes of a UUID and convert them to a readable string via a Base64 encoder. The result is a string of 22 characters instead of 32, which is particularly convenient for web services since it makes the URLs 10 characters shorter.

I’ve implemented a version of it in PHP. Just copy and paste the code below and call createBase64UUID()

function createBase64UUID() {
  $uuid com_create_guid();
  $byteString "";

  // Remove the dashes from the string
  $uuid str_replace("-"""$uuid);

  // Remove the opening and closing brackets
  $uuid substr($uuid1strlen($uuid) - 2); 

  // Read the UUID string byte by byte
  for($i 0$i strlen($uuid); $i += 2) {
    // Get two hexadecimal characters
    $s substr($uuid$i2);
    // Convert them to a byte
    $d hexdec($s);
    // Convert it to a single character
    $c chr($d);
    // Append it to the byte string
    $byteString $byteString.$c;
  } 

  // Convert the byte string to a base64 string
  $b64uuid base64_encode($byteString);
  // Replace the "/" and "+" since they are reserved characters
  $b64uuid str_replace("/""_"$b64uuid);
  $b64uuid str_replace("+""-"$b64uuid);
  // Remove the trailing "=="
  $b64uuid substr($b64uuid0strlen($b64uuid) - 2); 

  return $b64uuid;
}

Copyright © Pogopixels Ltd, 2008-2018