Windows 10 equivalent of LaunchAdvancedAssociationUI
Asked Answered
M

5

24

Since Windows 10, the IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI method does not work anymore.

On Windows Vista, 7 and 8, it opens the Control Panel on the Set Program Associations page for specified application.

On Windows 10, it does nothing.

It's even documented in Microsoft documentation:

Starting in Windows 10, this does not launch the association dialog box. It displays a dialog to the user informing them that they can change the default programs used to open file extensions in their Settings

(Even the second part of the statement is no longer true in the current version of Windows 10)


And actually in recent versions of Windows 10 that control panel does not exist anymore. Its functionality has been moved to a Settings app, under Apps > Default apps > Set defaults by app > [App name].

enter image description here

Is there a way to open the Set defaults by app screen for my application in Windows 10 Settings app programmatically?

Or is there another approach recommended for an application to allow its users to customize associations in Windows 10?

Mecklenburg answered 24/8, 2015 at 9:37 Comment(6)
maybe SHOpenWithDialog?Saviour
@Saviour I have no experience with the function, so I'm not sure if it fits my purpose. But anyway, see the remark in MSDN: Starting in Windows 10, the OAIF_ALLOW_REGISTRATION, OAIF_FORCE_REGISTRATION, and OAIF_HIDE_REGISTRATION flags will be ignored by SHOpenWithDialog. The Open With dialog box can no longer be used to change the default program used to open a file extension.Mecklenburg
Chromium use this api to change association on windows 8+. but I have not test it on windows 10. Anyway, you could look chromium's source code to find the standard way to change association.Saviour
Looked at the chromium's source code. It used your second method. So I guess there is no other better way to do the job.Saviour
@Saviour Thanks. I actually took the code from Mozilla source code. For webbrowser that's good enough because the SettingsPageAppsDefaults page allows changing the default webbrowser. So they do not need to look for a different solution (which means there might be). But for non-browser application it's not good enough.Mecklenburg
Related: Invoke ActivateApplication via C#Episodic
P
6

To open the Set your default programs page:

%windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram

Reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ee330741.aspx

Note: This method does not work with April 2018 Update.


To open the Choose default apps by file type page:

Activator->ActivateApplication(
    L"windows.immersivecontrolpanel_cw5n1h2txyewy"
    L"!microsoft.windows.immersivecontrolpanel",
    L"page=SettingsPageAppsDefaults"
    L"&target=SettingsPageAppsDefaultsFileExtensionView", AO_NONE, &pid);

Version 1709 or later

To open the Set defaults by app page:

Activator->ActivateApplication(
    L"windows.immersivecontrolpanel_cw5n1h2txyewy"
    L"!microsoft.windows.immersivecontrolpanel",
    L"page=SettingsPageAppsDefaults"
    L"&target=SettingsPageAppsDefaultsDefaultAppsListView", AO_NONE, &pid);

Windows 11 with 2023-04 Cumulative Update

ms-settings:defaultapps?registeredAppUser=YourAppRegName

or

ms-settings:defaultapps?registeredAppMachine=YourAppRegName

or

ms-settings:defaultapps?registeredAUMID=YourAppAUMID

Reference: https://learn.microsoft.com/en-us/windows/uwp/launch-resume/launch-default-apps-settings

Palmate answered 27/2, 2016 at 20:32 Comment(1)
Thanks! At least a step further to the final solution.Mecklenburg
D
5
  • Open the main Default Programs window in Control Panel:

    %windir%\system32\control.exe /name Microsoft.DefaultPrograms

  • Open the Set your default programs page:

    %windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram

  • Open the Set associations for a program page:

    %windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram\pageAdvancedSettings?pszAppName=YourAppRegName

    YourAppRegName is name of your registered application from HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)\SOFTWARE\RegisteredApplications that must be escaped (Use UrlEscape, Luke!) before use. For example:

    %windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram\pageAdvancedSettings?pszAppName=Internet%20Explorer

  • Open Associate a file type or protocol with a program page:

    %windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageFileAssoc

  • Open Change AutoPlay Settings page:

    %windir%\system32\control.exe /name Microsoft.AutoPlay

  • Open Set Program Access and Computer Defaults page:

    %windir%\system32\ComputerDefaults.exe

P.S. Also you can use IOpenControlPanel::Open method to open Control Panel item/page instead:

IOpenControlPanel * OpenControlPanel;

HRESULT Result =
  CoCreateInstance(CLSID_OpenControlPanel,
    NULL, CLSCTX_INPROC, __uuidof(IOpenControlPanel), (void**)&OpenControlPanel);
if (SUCCEEDED(Result))
{
  const wchar_t * Page = L"pageDefaultProgram\\pageAdvancedSettings?pszAppName=YourAppRegName";
  OpenControlPanel->Open(L"Microsoft.DefaultPrograms", Page, NULL);
  OpenControlPanel->Release();
}
Desiree answered 6/8, 2016 at 14:14 Comment(6)
Thanks. How did you find out the pageDefaultProgram\pageAdvancedSettings?pszAppName=YourAppRegName?Mecklenburg
Worth nothing that the pageDefaultProgram\pageAdvancedSettings?pszAppName=YourAppRegName works on Windows 7 too. But it does not seem to work on Windows Vista.Mecklenburg
@Martin Prikryl, IApplicationAssociationRegistrationUI interface is implemented in sud.dll library. I opened sud.dll in IDA Pro and try find string literals such as pageDefaultProgram. See function CApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI.Desiree
Related: Invoke 'control.exe' via C#Episodic
Starting from April 2018 Update, this method (or any other methods I know of) will no longer open [Set associations for a program] and [Set associations for a program]. [Default apps] setting will open instead.Palmate
Unfortunately, I have to unaccept the answer as @Palmate is correct, this no longer works.Mecklenburg
M
4

Changing the system default apps is no longer allowed. Here is the annoucement on the Windows Insider blog:

Changes to how Windows 10 handles default apps: ‘Default apps’ refers to the way that Windows maps file types and protocols (like HTTP) to the Windows applications they open by default. For example, your favorite photo editor may be set as the default app for .JPG files, which means that when you double-click on a .JPG file in File Explorer, it opens in that photo editor. In Windows 8.1, Classic Windows applications (Win32) could invoke the prompt asking you to change your defaults, so you may have seen multiple prompts during install and after they launched. However, Windows Store apps could not invoke this prompt. Instead, a notification banner will appear after your apps are installed telling you that new apps are available and you would click on this banner to change your defaults.

We know your defaults matter to you. With Windows 10, all apps – both Classic Windows apps and Universal Windows apps – will be unable to invoke a prompt to change your defaults, only Windows. You remain in full control of your default experiences, while reducing some of the unwanted noise that multiple prompts can bring.

Even if there is some way to launch the settings application, you will not be able to do more.

Minaret answered 11/2, 2016 at 12:57 Comment(0)
S
2

I managed to do it using UI Automation. It's not the ideal solution but it seems to work. Here is the code with comments inline:

#include <stdio.h>
#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <UIAutomationCore.h>
#include <UIAutomationClient.h>

// the main function
HRESULT OpenSetDefaultsByApp(LPCWSTR appName);

// helpers
HRESULT FindFirstChild(IUIAutomation *automation, IUIAutomationElement *element, PROPERTYID pid, VARIANT value, IUIAutomationElement **child);
HRESULT FindFirstChildInList(IUIAutomation *automation, IUIAutomationElement *list, PROPERTYID pid, VARIANT value, IUIAutomationElement **child);
HRESULT OpenSetDefaultsByApp();

// some useful macros for error handling
// uses wprintf so you might want to change it, if running in a non-console context
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}}

int main()
{
  CoInitialize(NULL);
  OpenSetDefaultsByApp(L"Google Chrome"); // pass the app name as it's displayed in app settings
  CoUninitialize();
}

HRESULT OpenSetDefaultsByApp(LPCWSTR appName)
{
  HRESULT hr = S_OK;
  CComBSTR name = appName;
  CComPtr<IUIAutomation> automation;
  CComPtr<IUIAutomationElement> root;
  CComPtr<IUIAutomationElement> settingsWindow;
  CComPtr<IUIAutomationElement> coreWindow;
  CComPtr<IUIAutomationElement> content;
  CComPtr<IUIAutomationElement> list;
  CComPtr<IUIAutomationElement> scrollViewer;
  CComPtr<IUIAutomationElement> appNameListItem;
  CComPtr<IUIAutomationElement> manageButton;
  CComPtr<IUIAutomationSelectionItemPattern> selection;
  CComPtr<IUIAutomationInvokePattern> invoke;

  // because setting windows and content are completely refreshed, we need two rounds
  // one to open the list of apps
  HRCHECK(OpenSetDefaultsByApp());

  // another one to select the app that starts now...
  // create UIA COM server and get root
  HRCHECK(automation.CoCreateInstance(CLSID_CUIAutomation8));
  HRCHECK(automation->GetRootElement(&root));

  // get hierarchy one by one. This is so it doesn't take too much time
  HRCHECK(FindFirstChild(automation, root, UIA_ClassNamePropertyId, CComVariant("ApplicationFrameWindow"), &settingsWindow));
  HRCHECK(FindFirstChild(automation, settingsWindow, UIA_ClassNamePropertyId, CComVariant("Windows.UI.Core.CoreWindow"), &coreWindow));
  HRCHECK(FindFirstChild(automation, coreWindow, UIA_AutomationIdPropertyId, CComVariant("pageContent"), &content));
  HRCHECK(FindFirstChild(automation, content, UIA_AutomationIdPropertyId, CComVariant("ItemsControlScrollViewer"), &scrollViewer));

  // now the list of app should be shown, get it
  HRCHECK(FindFirstChild(automation, scrollViewer, UIA_AutomationIdPropertyId, CComVariant("SystemSettings_DefaultApps_DefaultAppsList_ListView"), &list));

  // find the item by it's name
  // the list is virtualized so we use a helper method
  // note for some reason, the name is the name plus a space... 
  name.Append(" ");
  HRCHECK(FindFirstChildInList(automation, list, UIA_NamePropertyId, CComVariant(name), &appNameListItem));

  // we got the app item, select it so the 'Manage' button can appear
  HRCHECK(appNameListItem->GetCurrentPatternAs(UIA_SelectionItemPatternId, IID_PPV_ARGS(&selection)));
  if (!selection) HRCHECK(E_FAIL);
  HRCHECK(selection->Select());

  // get the 'Manage' button
  HRCHECK(FindFirstChild(automation, scrollViewer, UIA_ClassNamePropertyId, CComVariant("Button"), &manageButton));

  // press the 'Manage' button
  HRCHECK(manageButton->GetCurrentPatternAs(UIA_InvokePatternId, IID_PPV_ARGS(&invoke)));
  if (!invoke) HRCHECK(E_FAIL);
  HRCHECK(invoke->Invoke());

cleanup:
  return hr;
}

HRESULT OpenSetDefaultsByApp()
{
  HRESULT hr = S_OK;
  CComPtr<IUIAutomation> automation;
  CComPtr<IUIAutomationElement> root;
  CComPtr<IUIAutomationElement> settingsWindow;
  CComPtr<IUIAutomationElement> coreWindow;
  CComPtr<IUIAutomationElement> content;
  CComPtr<IUIAutomationElement> setDefaultsByAppLink;
  CComPtr<IUIAutomationInvokePattern> invoke;

  // create UIA COM server and get root
  HRCHECK(automation.CoCreateInstance(CLSID_CUIAutomation8));
  HRCHECK(automation->GetRootElement(&root));

  // show up to the deepest we can
  WinExec("control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram", SW_NORMAL);

  // find the 'Set defaults by app' link (button).
  HRCHECK(FindFirstChild(automation, root, UIA_ClassNamePropertyId, CComVariant("ApplicationFrameWindow"), &settingsWindow));
  HRCHECK(FindFirstChild(automation, settingsWindow, UIA_ClassNamePropertyId, CComVariant("Windows.UI.Core.CoreWindow"), &coreWindow));
  HRCHECK(FindFirstChild(automation, coreWindow, UIA_AutomationIdPropertyId, CComVariant("pageContent"), &content));
  HRCHECK(FindFirstChild(automation, content, UIA_AutomationIdPropertyId, CComVariant("SettingsPageAppsDefaultsDefaultAppsListView_HyperlinkButton"), &setDefaultsByAppLink));

  // yes, so press this button
  HRCHECK(setDefaultsByAppLink->GetCurrentPatternAs(UIA_InvokePatternId, IID_PPV_ARGS(&invoke)));
  if (!invoke) HRCHECK(E_FAIL);
  HRCHECK(invoke->Invoke());

cleanup:
  return hr;
}

// this method has retries with timeouts included, so it's much better than a raw call to FindFirst
HRESULT FindFirstChild(IUIAutomation *automation, IUIAutomationElement *element, PROPERTYID pid, VARIANT value, IUIAutomationElement **child)
{
  HRESULT hr = S_OK;
  int timeout = 5000; // max timeout is defined here as 5 sec. This should be ok for most machines
  int slice = 100; // time between too retries, defined as 100 ms.
  int time = 0;
  CComPtr<IUIAutomationCondition> condition;
  HRCHECK(automation->CreatePropertyCondition(pid, value, &condition));

  do
  {
    // I used SubTree here, this may not be appropriate in all context
    // for performance issues. In fact, this could be passed as a parameter...
    hr = element->FindFirst(TreeScope_Subtree, condition, child);
    if (*child) break;
    time += slice;
    if (time >= timeout) HRCHECK(E_FAIL);
    Sleep(slice);
  } while (TRUE);

cleanup:
  return hr;
}

// this helper supports virtualized list
HRESULT FindFirstChildInList(IUIAutomation *automation, IUIAutomationElement *list, PROPERTYID pid, VARIANT value, IUIAutomationElement **child)
{
  HRESULT hr = S_OK;
  CComBSTR lastName;
  int lastNameCount = 0;
  CComPtr<IUIAutomationCondition> trueCondition;
  HRCHECK(automation->CreateTrueCondition(&trueCondition));

  do
  {
    // get all children
    CComPtr<IUIAutomationElementArray> all;
    HRCHECK(list->FindAll(TreeScope_Children, trueCondition, &all));

    int count;
    HRCHECK(all->get_Length(&count));
    if (count == 0) continue; // there shouldn't be zero element, so go on scanning

    for (int i = 0; i < count; i++)
    {
      // test each element for the searched property
      CComPtr<IUIAutomationElement> element;
      HRCHECK(all->GetElement(i, &element));

      CComVariant v;
      HRCHECK(element->GetCurrentPropertyValue(pid, &v));
      if (VarCmp(&v, &value, 0) == 1)
      {
        HRCHECK(element.QueryInterface(child));
        goto cleanup;
      }
    }

    // not found in the current page/set, go to last element and scroll it into view to force list to load the next
    CComPtr<IUIAutomationElement> last;
    CComPtr<IUIAutomationScrollItemPattern> pattern;
    HRCHECK(all->GetElement(count - 1, &last));

    // check if we didn't progress (same name for 20 rounds)
    CComBSTR name;
    HRCHECK(last->get_CurrentName(&name));
    if (name == lastName)
    {
      lastNameCount++;
      if (lastNameCount > 20) HRCHECK(E_FAIL); // not found!
    }
    else
    {
      lastNameCount = 0;
    }
    lastName = name;

    HRCHECK(last->GetCurrentPatternAs(UIA_ScrollItemPatternId, IID_PPV_ARGS(&pattern)));
    if (!pattern) HRCHECK(E_FAIL);
    HRCHECK(pattern->ScrollIntoView());
  } while (TRUE);

cleanup:
  return hr;
}
Sodom answered 6/9, 2018 at 7:35 Comment(1)
Thanks. As I wrote above, I'm happy for this workaround, but it's not an answer I would accept. But definitely worth an upvote and the bounty.Mecklenburg
P
1
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "%windir%\system32\control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram\pageAdvancedSettings?pszAppName=Internet%20Explorer"
' Give Default Programs time to load
WScript.Sleep 1200
' WshShell.AppActivate "Set Program Associations to IE then end for Windows 10 enjoy! ~ The Dogs Trust Rich ~"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys " "
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys " "
msgbox "Internet Explorer is now your default browser"
WScript.Quit
Permian answered 27/8, 2016 at 7:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.