【问题标题】:Windows 10 equivalent of LaunchAdvancedAssociationUIWindows 10 等效于 LaunchAdvancedAssociationUI
【发布时间】:2026-01-08 19:20:05
【问题描述】:

自 Windows 10 起,IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI method 不再工作。

在 Windows Vista、7 和 8 上,它会在指定应用程序的设置程序关联页面上打开控制面板。

在 Windows 10 上,它什么也不做。

它甚至记录在 Microsoft 文档中:

从 Windows 10 开始,这不会启动关联对话框。它会向用户显示一个对话框,通知他们可以在其设置

中更改用于打开文件扩展名的默认程序

(在当前版本的 Windows 10 中,即使是陈述的第二部分也不再适用)


实际上,在最新版本的 Windows 10 中,该控制面板不再存在。其功能已移至“设置”应用,位于 应用 > 默认应用 > 按应用设置默认值 > [应用名称]

有没有办法以编程方式在 Windows 10 设置应用程序中打开我的应用程序的按应用程序设置默认值屏幕?

或者是否有其他方法推荐给应用程序以允许其用户在 Windows 10 中自定义关联?

【问题讨论】:

  • 也许是 SHOpenWithDialog?
  • @Jichao 我对这个功能没有经验,所以我不确定它是否符合我的目的。但无论如何,请参阅MSDN 中的注释:从 Windows 10 开始,SHOpenWithDialog 将忽略 OAIF_ALLOW_REGISTRATION、OAIF_FORCE_REGISTRATION 和 OAIF_HIDE_REGISTRATION 标志。 “打开方式”对话框不能再用于更改用于打开文件扩展名的默认程序。
  • Chromium 使用此 api 更改 windows 8+ 上的关联。但我没有在 Windows 10 上测试过。无论如何,你可以查看 chromium 的源代码以找到更改关联的标准方法。
  • 看了一下chromium的源码。它使用了您的第二种方法。所以我想没有其他更好的方法来完成这项工作。

标签: windows winapi windows-10 windows-shell controlpanel


【解决方案1】:
  • 在控制面板中打开默认程序主窗口:

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

  • 打开设置您的默认程序页面:

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

  • 打开为程序设置关联页面:

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

    YourAppRegName 是您在 HKEY_LOCAL_MACHINEHKEY_CURRENT_USER中注册的应用程序的名称)\SOFTWARE\RegisteredApplications,在使用前必须转义(使用UrlEscape,Luke!)。例如:

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

  • 打开将文件类型或协议与程序相关联页面:

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

  • 打开更改自动播放设置页面:

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

  • 打开设置程序访问和计算机默认值页面:

    %windir%\system32\ComputerDefaults.exe

附:您也可以使用IOpenControlPanel::Open 方法打开控制面板项/页面:

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();
}

【讨论】:

  • 谢谢。你是怎么找到pageDefaultProgram\pageAdvancedSettings?pszAppName=YourAppRegName的?
  • pageDefaultProgram\pageAdvancedSettings?pszAppName=YourAppRegName 也可以在 Windows 7 上运行,这一点毫无价值。但它似乎不适用于 Windows Vista。
  • @Martin Prikryl,IApplicationAssociationRegistrationUI 接口在 sud.dll 库中实现。我在 IDA Pro 中打开 sud.dll 并尝试查找字符串文字,例如 pageDefaultProgram。见函数CApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI
  • 从 2018 年 4 月更新开始,此方法(或我知道的任何其他方法)将不再打开 [为程序设置关联] 和 [为程序设置关联]。 [默认应用]设置将改为打开。
【解决方案2】:

打开设置您的默认程序页面:

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

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

注意:此方法不适用于 2018 年 4 月更新。


打开按文件类型选择默认应用页面:

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

版本 1709 或更高版本

打开按应用设置默认值页面:

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

【讨论】:

  • 谢谢!至少比最终解决方案更进一步。
【解决方案3】:

不再允许更改系统默认应用程序。这是Windows Insider blog上的公告:

对 Windows 10 处理默认应用程序的方式的更改:“默认应用程序”是指 Windows 将文件类型和协议(如 HTTP)映射到默认打开的 Windows 应用程序的方式。例如,您最喜欢的照片编辑器可能被设置为 .JPG 文件的默认应用程序,这意味着当您在文件资源管理器中双击 .JPG 文件时,它会在该照片编辑器中打开。在 Windows 8.1 中,经典 Windows 应用程序 (Win32) 可能会调用提示,要求您更改默认设置,因此您可能在安装期间和启动后看到多个提示。但是,Windows 应用商店应用程序无法调用此提示。相反,在您的应用安装后会出现一个通知横幅,告诉您有新应用可用,您可以点击此横幅更改您的默认设置。

我们知道您的默认设置对您很重要。在 Windows 10 中,所有应用(经典 Windows 应用和通用 Windows 应用)都将无法调用提示来更改默认设置,只有 Windows。您可以完全控制您的默认体验,同时减少多个提示可能带来的一些不必要的噪音。

即使有一些方法可以启动设置应用程序,您也无法进行更多操作。

【讨论】:

    【解决方案4】:

    我设法使用UI Automation 做到了。这不是理想的解决方案,但似乎有效。这是内联 cmets 的代码:

    #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;
    }
    

    【讨论】:

    • 谢谢。正如我在上面所写的,我很高兴这种解决方法,但这不是我会接受的答案。但绝对值得一票和赏金。
    【解决方案5】:
    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
    

    【讨论】:

      最近更新 更多