"Open With" handler selection

We have a product that allows the user to open files, with the default handler this is easy, but we wanted an “Open With” selection for the handler. Initially we trawled through the registry and presented a menu of items that were registered to open a particular file extension, but this was very inconsistent.
In the end I thought why not just use the Windows OpenWith.exe, and this has been working fine using ShellExecute. (I have also tried CreateProcess, with different options).
Until now. OpenWith.exe no longer works in Windows 11. It works fine in Windows Explorer, and I can see the process fire up, but no menu appears, and it closes immediately. If I use an older version of OpenWith.exe it still works as expected. MS have changed something.
Thoughts on this? Does anyone have a good way of providing this functionality they wouldn’t mind sharing?
I have found: Enumerating all the programs that can open a particular file extension - The Old New Thing, but I don’t have the time nor patience at the moment to translate…

2 Likes

@chuacw might have your answer …

But I’m gunna say I’m actually kinda stunned that ChatGPT seems to be useful here.

I asked it to translate this :

#include <windows.h>
#include <ole2.h>
#include <shlobj.h>
#include <atlbase.h>
#include <atlalloc.h>
#include <vector>
#include <iostream>

std::vector<CComPtr<IAssocHandler>> 
            LoadHandlers( PCWSTR extension, ASSOC_FILTER filter)
{
  std::vector<CComPtr<IAssocHandler>> handlers;
  CComPtr<IEnumAssocHandlers>         enumerator;
  SHAssocEnumHandlers(extension, filter, &enumerator);
  for (  CComPtr<IAssocHandler> handler;
         enumerator->Next(1, &handler, nullptr) == S_OK;
         handler.Release() 
      )  { handlers.push_back(handler); }
  return handlers;
}

and it gave me (after a tiny amount of fix-up) :

{$APPTYPE CONSOLE}
program Project1;
uses
  Windows, ActiveX, ShlObj, ComObj, Classes, SysUtils;
type
   arr = array of IAssocHandler;
function
   LoadHandlers(const Extension : LPWSTR;
                         Filter : TAssocFilter) : arr;
var
  Handlers   : arr;
  Handler    : IAssocHandler;
  Enumerator : IEnumAssocHandlers;
  _          : cardinal;               //  GPT used 'nil'  
begin
  SetLength(Handlers, 0);
  SHAssocEnumHandlers(Extension, Filter, Enumerator);
  while Enumerator.Next(1, Handler, _ ) = S_OK do
        begin
            SetLength(Handlers, Length(Handlers) + 1);
            Handlers[High(Handlers)] := Handler;
            Handler := nil;
        end;
  Result := Handlers;
end;

And it seems to work.

The second part didn’t come out so clean … but a lot of it is unnecessary.
So I just added this at the end of the function above :

  for var i := 0 to length(handlers) - 1 do
  begin
      var name : LPWSTR;
      handlers[i].GetUIName(name);
      writeln( i, ': ', name);
  end;

and LoadHandlers( '.docx', ASSOC_FILTER_RECOMMENDED); returned :

0: Word
1: File Viewer Plus 3
2: The main calibre program
3: The calibre e-book editor
4: The calibre e-book viewer
5: Thunderbird

1 Like

Excellent thanks! That returns the same list as Windows Explorer.

1 Like