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.

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.