Windows通知中心Zone App通知开关按钮失效问题求助
Zone App通知中心开关无法切换状态的问题分析与修复
核心问题原因
- 注册表配置错误
- HKLM下
SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\{{#MyAppID}}的Enabled值设为0,全局禁用了应用推送通知,导致开关状态无法正常同步。 - 多处注册表项模板语法存在括号缺失(如
{{#MyAppId}未闭合为{{#MyAppId}}),导致实际注册的路径无效。 ToastActivatorCLSID的注册表值格式不规范,且未与DLL中定义的GUID完全匹配。
- HKLM下
- COM服务器接口缺失
当前DummyToastActivator仅实现了IUnknown接口,但Windows通知中心要求应用实现IToastNotificationActivationCallback接口来处理开关状态变更的系统回调,缺少该接口会导致开关状态无法持久化。 - 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
相关产品推荐
相关产品推荐

