10

I want to let the user select an association (open with) for an currently unregistered file extension.

Currently I'm telling the API to open the file by using ShellExecute and it returns an ERROR_NO_ASSOCIATION error code.

Is there a way to tell the API that it should let the user select a new association?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Tobias R
  • 928
  • 8
  • 22

3 Answers3

12

I use

procedure ShellOpenAs(const AFileName: string; AHandle: HWND);
begin
  ShellExecute(AHandle, 'open', PChar('rundll32.exe'), PChar('shell32.dll,OpenAs_RunDLL ' + AFileName), nil, SW_SHOWNORMAL);
end;

Edit (inspired by David's comment and https://stackoverflow.com/a/13229516/1431618): One can omit ShellExecute and RunDll32 by calling OpenAs_RunDLL directly:

procedure OpenAs_RunDLL(hwnd: HWND; hinst: HINST; lpszCmdLine: LPSTR; nCmdShow: Integer); stdcall; external shell32;

procedure ShellOpenAs(AHandle: HWND; const AFileName: string);
begin
  OpenAs_RunDLL(AHandle, HInstance, PChar(AFileName), SW_SHOWNORMAL);
end;

There is also SHOpenWithDialog on Windows Vista and later. (I find it interesting that Microsoft wrote a RunDLL compatible entry point but until Vista didn't bother to provide a regular API function.)

Community
  • 1
  • 1
Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • Thanks, I'll add this as the fallback if the first fails, since I only want the dialog if I can't already open it. – Tobias R Feb 10 '12 at 10:04
  • 1
    The correct way to invoke the dialog is to use the `openas` verb instead: `ShellExecute(AHandle, 'openas', PChar(AFileName), nil, nil, SW_SHOWNORMAL); ` – Remy Lebeau Feb 10 '12 at 21:22
  • @Remy: This doesn't work for me (Windows 7 64-bit). I get system error 1155 - translated from German: "The specified file has no associated application". – Uli Gerhardt Feb 13 '12 at 08:53
  • @UlrichGerhardt: `openas` works for me on my Win7 64-bit machine, both for `ShellExecute()` and `ShellExecuteEx()`. – Remy Lebeau Feb 14 '12 at 00:51
  • 1
    Would be easier to call `OpenAs_RunDLL` directly rather than involving a call to `ShellExecute`. – David Heffernan Nov 05 '12 at 11:27
  • Note that `OpenAs_RunDLL` is undocumented and [does not always work.](http://stackoverflow.com/questions/23566667/rundll32-shell32-dll) – Harry Johnston May 10 '14 at 01:37
4

Go with following code you will get your solution-

public const uint SEE_MASK_INVOKEIDLIST = 12;//add this line in your code


CoInitializeEx(NULL, COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.nShow = SW_SHOWNORMAL;
sei.lpVerb = "openas";
sei.lpFile = "C:\\yourfile.ext";
sei.lfmask= SEE_MASK_INVOKEIDLIST;//add this line in your code
ShellExecuteEx(&sei);

SEE_MASK_INVOKEIDLIST this variable set "Verb" from presented system registry.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
  • This is the correct answer for my scenario (not necessarily the same as the OP), which is to show the "Select an app to open.." dialog, in this case on Windows 11. – Dave Nottage Jun 22 '23 at 00:21
3

Simply do not use explicit verb. Using a specific verb like 'open' is a big mistake:

  • 'open' may be not a default verb (for example, it may be 'play', 'edit' or 'run')
  • 'open' may not exists

It is a way more correct to simply pass nil as verb. The system will automatically select most appropriate verb:

  • Default verb will be used, if it is set
  • 'open' verb will be used, if no default verb is set
  • first verb will be used, if no default and 'open' verbs are available
  • if no verbs are assigned - the system will bring "Open with" dialog

In other words, simple

ShellExecute(0, nil, 'C:\MyFile.StrangeExt', ...);

will show "Open with" dialog.

Only use a specific verb if you want a specific action. E.g. 'print', 'explore', 'runas'. Otherwise - just pass nil.

Alex
  • 5,477
  • 2
  • 36
  • 56