You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows通知中心Zone App通知开关按钮失效问题求助

Zone App通知中心开关无法切换状态的问题分析与修复

核心问题原因

  1. 注册表配置错误
    • HKLM下SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\{{#MyAppID}}的Enabled值设为0,全局禁用了应用推送通知,导致开关状态无法正常同步。
    • 多处注册表项模板语法存在括号缺失(如{{#MyAppId}未闭合为{{#MyAppId}}),导致实际注册的路径无效。
    • ToastActivatorCLSID的注册表值格式不规范,且未与DLL中定义的GUID完全匹配。
  2. COM服务器接口缺失
    当前DummyToastActivator仅实现了IUnknown接口,但Windows通知中心要求应用实现IToastNotificationActivationCallback接口来处理开关状态变更的系统回调,缺少该接口会导致开关状态无法持久化。
  3. DLL注册路径硬编码
    DllRegisterServer中硬编码了DLL安装路径,若用户自定义安装目录,会导致COM服务器无法被系统正确加载。

修复步骤

1. 修正.iss安装脚本的注册表配置

  • 将HKLM下PushNotifications的Enabled值改为1,启用全局推送权限
  • 修复所有模板语法的括号错误
  • 确保ToastActivatorCLSID值与DLL中的GUID一致

修正后的[Registry]段示例:

[Registry]
Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\{{#MyAppId}}"; ValueName: "Enabled"; ValueType: dword; ValueData: 1; Flags: createvalueifdoesntexist uninsdeletekey
Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\{{#MyAppId}}"; ValueName: "ToastEnabled"; ValueType: dword; ValueData: 1; Flags: createvalueifdoesntexist uninsdeletekey
Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\{{#MyAppId}}"; ValueName: "DisplayName"; ValueType: string; ValueData: "{#MyAppName}"; Flags: createvalueifdoesntexist uninsdeletekey

Root: HKLM; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\{{#MyAppID}}"; ValueType: string; ValueName: "DisplayName"; ValueData: "{#MyAppName}"
Root: HKLM; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\{{#MyAppID}}"; ValueType: string; ValueName: "Description"; ValueData: "Zone App Description"
Root: HKLM; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\{{#MyAppID}}"; ValueType: dword; ValueName: "Enabled"; ValueData: 1
Root: HKCU; Subkey: "Software\Classes\AppUserModelId\{{#MyAppId}}"; ValueName: "DisplayName"; ValueType: string; ValueData: "{#MyAppName}"; Flags: createvalueifdoesntexist uninsdeletekey
Root: HKCU; Subkey: "Software\Classes\AppUserModelId\{{#MyAppId}}"; ValueName: "ToastActivatorCLSID"; ValueType: string; ValueData: "{95DA8DD5-7867-4A94-AAEC-FC363363C04A}"; Flags: createvalueifdoesntexist uninsdeletekey

2. 实现正确的COM接口(ToastNotifier.dll)

修改C++代码,让激活类实现IToastNotificationActivationCallback接口,处理开关状态变更的回调:

#include "pch.h"
#include <Windows.h>
#include <objbase.h>
#include <wrl.h>
using namespace Microsoft::WRL;

// 声明通知激活回调接口
MIDL_INTERFACE("53E31837-6600-4A81-9395-75CFFE746F94")
IToastNotificationActivationCallback : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE Activate(
        LPCWSTR appUserModelId,
        LPCWSTR invokedArgs,
        const NOTIFICATION_USER_INPUT_DATA* data,
        ULONG dataCount) = 0;
};

class ToastActivator : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IToastNotificationActivationCallback>
{
public:
    HRESULT STDMETHODCALLTYPE Activate(
        LPCWSTR appUserModelId,
        LPCWSTR invokedArgs,
        const NOTIFICATION_USER_INPUT_DATA* data,
        ULONG dataCount) override
    {
        // 处理开关状态变更,更新注册表
        if (data && dataCount > 0)
        {
            for (ULONG i = 0; i < dataCount; i++)
            {
                if (wcscmp(data[i].Key, L"toggleNotification") == 0)
                {
                    HKEY hKey;
                    if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings\\{{#MyAppId}}", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
                    {
                        DWORD enabled = wcscmp(data[i].Value, L"on") == 0 ? 1 : 0;
                        RegSetValueExW(hKey, L"Enabled", 0, REG_DWORD, (BYTE*)&enabled, sizeof(DWORD));
                        RegCloseKey(hKey);
                    }
                }
            }
        }
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override
    {
        if (riid == IID_IUnknown || riid == __uuidof(IToastNotificationActivationCallback))
        {
            *ppvObject = static_cast<IToastNotificationActivationCallback*>(this);
            AddRef();
            return S_OK;
        }
        *ppvObject = nullptr;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef() override
    {
        return InterlockedIncrement(&refCount);
    }

    ULONG STDMETHODCALLTYPE Release() override
    {
        LONG ref = InterlockedDecrement(&refCount);
        if (ref == 0) delete this;
        return ref;
    }

private:
    LONG refCount = 1;
};

// 保持原GUID不变
const CLSID CLSID_ToastActivator = { 0x95DA8DD5, 0x7867, 0x4A94, { 0xAA, 0xEC, 0xFC, 0x36, 0x33, 0x63, 0xC0, 0x4A } };

const wchar_t* CLSID_KEY = L"Software\\Classes\\CLSID\\{95DA8DD5-7867-4A94-AAEC-FC363363C04A}";
const wchar_t* INPROC_SERVER_KEY = L"Software\\Classes\\CLSID\\{95DA8DD5-7867-4A94-AAEC-FC363363C04A}\\InProcServer32";
const wchar_t* ACTIVATION_CATEGORY_KEY = L"Software\\Classes\\CLSID\\{95DA8DD5-7867-4A94-AAEC-FC363363C04A}\\ActivationCategory";

extern "C" __declspec(dllexport) HRESULT WINAPI DllRegisterServer()
{
    HKEY hKey;
    wchar_t dllPath[MAX_PATH];
    GetModuleFileNameW(GetModuleHandleW(L"ToastNotifier.dll"), dllPath, MAX_PATH);

    // 注册CLSID
    if (RegCreateKeyExW(HKEY_CURRENT_USER, CLSID_KEY, 0, nullptr, 0, KEY_WRITE, nullptr, &hKey, nullptr) == ERROR_SUCCESS)
    {
        RegSetValueExW(hKey, nullptr, 0, REG_SZ, (BYTE*)L"Zone App Toast Activator", (wcslen(L"Zone App Toast Activator") + 1) * sizeof(wchar_t));
        RegCloseKey(hKey);
    }

    // 注册InProcServer32,使用动态路径
    if (RegCreateKeyExW(HKEY_CURRENT_USER, INPROC_SERVER_KEY, 0, nullptr, 0, KEY_WRITE, nullptr, &hKey, nullptr) == ERROR_SUCCESS)
    {
        RegSetValueExW(hKey, nullptr, 0, REG_SZ, (BYTE*)dllPath, (wcslen(dllPath) + 1) * sizeof(wchar_t));
        RegSetValueExW(hKey, L"ThreadingModel", 0, REG_SZ, (BYTE*)L"Apartment", (wcslen(L"Apartment") + 1) * sizeof(wchar_t));
        RegCloseKey(hKey);
    }

    // 设置激活类别为通知激活器
    if (RegCreateKeyExW(HKEY_CURRENT_USER, ACTIVATION_CATEGORY_KEY, 0, nullptr, 0, KEY_WRITE, nullptr, &hKey, nullptr) == ERROR_SUCCESS)
    {
        DWORD category = 4;
        RegSetValueExW(hKey, nullptr, 0, REG_DWORD, (BYTE*)&category, sizeof(DWORD));
        RegCloseKey(hKey);
    }

    return S_OK;
}

extern "C" __declspec(dllexport) HRESULT WINAPI DllUnregisterServer()
{
    RegDeleteKeyW(HKEY_CURRENT_USER, ACTIVATION_CATEGORY_KEY);
    RegDeleteKeyW(HKEY_CURRENT_USER, INPROC_SERVER_KEY);
    RegDeleteKeyW(HKEY_CURRENT_USER, CLSID_KEY);
    return S_OK;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hModule);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

3. 修复DLL注册路径问题

使用GetModuleFileNameW获取DLL的实际安装路径,替代硬编码路径,确保在任意安装目录下都能正确注册COM服务器。

4. Dart层同步状态

在Dart代码中,通过调用原生方法读取注册表中Enabled值,在应用启动时初始化通知状态,并监听注册表变化以同步开关状态。


内容的提问来源于stack exchange,提问作者Ashish Rana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:47:32