Raymond Chen wrote the original blog posts on enumerating all the programs that can launch a particular protocol and enumerating all the programs that can open a particular file extension.

I translated it to Delphi. The application below does 2 things: launching a web browser on Embarcadero and opening win.ini as a text file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
program Handlers;
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils,
  System.Generics.Collections,
  Winapi.ShlObj, System.Types, Winapi.ActiveX, idoc;
 
function GetUIObjectOfFile(Handle: THandle; const Path: string;
  const RIID: TGUID; var P): Boolean;
var
  ppidl, pidlChild: PItemIDList;
  psfgaoOut: DWORD;
  LShellFolder: IShellFolder;
begin
  Result := False;
  if Succeeded(SHParseDisplayName(PChar(Path), nil, ppidl, 0, psfgaoOut)) then
    begin
      if Succeeded(SHBindToParent(ppidl, IID_IShellFolder, Pointer(LShellFolder), pidlChild)) then
         begin
           Result := Succeeded(LShellFolder.GetUIObjectOf(Handle, 1, pidlChild, RIID, nil, Pointer(P)));
         end;
      CoTaskMemFree(ppidl);
    end;
end;
 
function LoadExtHandlers(const extension: string;
  filter: ASSOC_FILTER): TList<IAssocHandler>
var
  enumerator: IEnumAssocHandlers;
  Fetched: Cardinal;
  handler: IAssocHandler;
  LName: PChar;
begin
  Result := nil;
  SHAssocEnumHandlers(PChar(extension), filter, enumerator);
  repeat
    Fetched := 0;
    enumerator.Next(1, handler, Fetched);
    if Fetched<>0 then
      begin
        handler.GetName(LName);
        WriteLn('Handler: ', LName);
        if not Assigned(Result) then
          Result := TList.Create;
        Result.Add(handler);
      end;
  until Fetched = 0;
end;
 
function LoadProtocolHandlers(const protocol: string; filter: ASSOC_FILTER):
  TList<IAssocHandler>;
var
  enumerator: IEnumAssocHandlers;
  AliasToEnumerator: Pointer absolute enumerator;
  Fetched: Cardinal;
  handler: IAssocHandler;
  LName: PChar;
begin
  Result := nil;
  SHAssocEnumHandlersForProtocolByApplication(PChar(protocol),
    IID_IEnumAssocHandlers, AliasToEnumerator);
  repeat
    Fetched := 0;
    enumerator.Next(1, handler, Fetched);
    if Fetched<>0 then
      begin
        handler.GetName(LName);
        WriteLn('Handler: ', LName);
        if not Assigned(Result) then
          Result := TList.Create;
        Result.Add(handler);
      end;
  until Fetched = 0;
end;
 
var
  LProtocolHandlers, LExtHandlers: TList<IAssocHandler>;
  LDataObject: IDataObject;
  selection: Integer;
begin
  selection := 0;
 
  LProtocolHandlers := LoadProtocolHandlers('http', ASSOC_FILTER_RECOMMENDED);
  GetUIObjectOfFile(0, 'http://www.embarcadero.com', IID_IDataObject, LDataObject);
  LProtocolHandlers[selection].Invoke(LDataObject);
  LProtocolHandlers.Free;
 
  LExtHandlers := LoadExtHandlers('.txt', ASSOC_FILTER_RECOMMENDED);
  GetUIObjectOfFile(0, 'C:\windows\win.ini', IID_IDataObject, LDataObject);
  LExtHandlers[selection].Invoke(LDataObject);
  LExtHandlers.Free;
end.