diff --git a/WindowsAppRuntime.sln b/WindowsAppRuntime.sln index 4d1ffadf9d..0199bf2073 100644 --- a/WindowsAppRuntime.sln +++ b/WindowsAppRuntime.sln @@ -179,6 +179,13 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PushNotifications", "PushNotifications", "{A1B25DCF-6A54-414D-8E24-F4D24EE9299D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PushNotificationsLongRunningTask.Msix", "test\PushNotifications\PushNotificationsLongRunningTask.Msix\PushNotificationsLongRunningTask.Msix.vcxproj", "{C422B090-F501-4204-8068-1084B0799405}" + ProjectSection(ProjectDependencies) = postProject + {9C1A6C58-52D6-4514-9120-5C339C5DF4BE} = {9C1A6C58-52D6-4514-9120-5C339C5DF4BE} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PushNotificationsLongRunningTask.ProxyStub", "dev\PushNotifications\PushNotificationsLongRunningTask.ProxyStub\PushNotificationsLongRunningTask.ProxyStub.vcxproj", "{BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PushNotificationsLongRunningTask.StartupTask", "dev\PushNotifications\PushNotificationsLongRunningTask.StartupTask\PushNotificationsLongRunningTask.StartupTask.vcxproj", "{1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerNotifications", "PowerNotifications", "{E9117D76-63AC-4289-A628-AB39EF6E5D19}" EndProject @@ -244,6 +251,7 @@ Global dev\EnvironmentManager\API\Microsoft.Process.Environment.vcxitems*{2f3fad1b-d3df-4866-a3a3-c2c777d55638}*SharedItemsImports = 9 dev\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxitems*{56371ca6-144b-4989-a4e9-391ad4fa7651}*SharedItemsImports = 9 test\inc\inc.vcxitems*{56a1d696-feda-4333-bf37-772ebececb10}*SharedItemsImports = 4 + test\inc\inc.vcxitems*{5b2d17fe-c371-417f-860c-3d32397c2404}*SharedItemsImports = 4 test\inc\inc.vcxitems*{7c502995-59c3-483b-86ba-815985353633}*SharedItemsImports = 4 test\inc\inc.vcxitems*{8e52d7ea-a200-4a6b-ba74-8efb49468caf}*SharedItemsImports = 4 test\inc\inc.vcxitems*{b567fe2e-3a03-48d0-b2b5-760cdec35891}*SharedItemsImports = 9 @@ -641,8 +649,8 @@ Global {0A5FEE93-48B7-40EC-BB9A-B27D11060DA9}.Release|x86.ActiveCfg = Release|Win32 {0A5FEE93-48B7-40EC-BB9A-B27D11060DA9}.Release|x86.Build.0 = Release|Win32 {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|ARM64.ActiveCfg = Debug|Win32 - {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|ARM64.Build.0 = Debug|Win32 + {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|ARM64.Build.0 = Debug|ARM64 {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|x64.ActiveCfg = Debug|x64 {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|x64.Build.0 = Debug|x64 {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0}.Debug|x86.ActiveCfg = Debug|Win32 @@ -666,6 +674,34 @@ Global {C422B090-F501-4204-8068-1084B0799405}.Release|x64.Build.0 = Release|x64 {C422B090-F501-4204-8068-1084B0799405}.Release|x86.ActiveCfg = Release|Win32 {C422B090-F501-4204-8068-1084B0799405}.Release|x86.Build.0 = Release|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|ARM64.Build.0 = Debug|ARM64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|x64.ActiveCfg = Debug|x64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|x64.Build.0 = Debug|x64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|x86.ActiveCfg = Debug|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Debug|x86.Build.0 = Debug|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|Any CPU.ActiveCfg = Release|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|ARM64.ActiveCfg = Release|ARM64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|ARM64.Build.0 = Release|ARM64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|x64.ActiveCfg = Release|x64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|x64.Build.0 = Release|x64 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|x86.ActiveCfg = Release|Win32 + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0}.Release|x86.Build.0 = Release|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|ARM64.Build.0 = Debug|ARM64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|x64.ActiveCfg = Debug|x64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|x64.Build.0 = Debug|x64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|x86.ActiveCfg = Debug|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Debug|x86.Build.0 = Debug|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|Any CPU.ActiveCfg = Release|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|ARM64.ActiveCfg = Release|ARM64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|ARM64.Build.0 = Release|ARM64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|x64.ActiveCfg = Release|x64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|x64.Build.0 = Release|x64 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|x86.ActiveCfg = Release|Win32 + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42}.Release|x86.Build.0 = Release|Win32 {CBD95746-61CE-4F31-B6CC-C5ABF1766180}.Debug|Any CPU.ActiveCfg = Debug|Win32 {CBD95746-61CE-4F31-B6CC-C5ABF1766180}.Debug|ARM64.ActiveCfg = Debug|Win32 {CBD95746-61CE-4F31-B6CC-C5ABF1766180}.Debug|x64.ActiveCfg = Debug|x64 @@ -936,6 +972,8 @@ Global {1307DD1B-BBE8-4CD0-B1A0-0DB6D61EEAA0} = {91D03B95-1B0C-4BEB-8441-30DA7D615538} {A1B25DCF-6A54-414D-8E24-F4D24EE9299D} = {8630F7AA-2969-4DC9-8700-9B468C1DC21D} {C422B090-F501-4204-8068-1084B0799405} = {A1B25DCF-6A54-414D-8E24-F4D24EE9299D} + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0} = {91D03B95-1B0C-4BEB-8441-30DA7D615538} + {1DEBBFF6-EE6E-4944-9DE2-35B7A686AF42} = {91D03B95-1B0C-4BEB-8441-30DA7D615538} {E9117D76-63AC-4289-A628-AB39EF6E5D19} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A} {B75C1B22-553C-40E4-B38E-6AB4D01FDB9D} = {E9117D76-63AC-4289-A628-AB39EF6E5D19} {CBD95746-61CE-4F31-B6CC-C5ABF1766180} = {8630F7AA-2969-4DC9-8700-9B468C1DC21D} diff --git a/build/CopyFilesToStagingDir.ps1 b/build/CopyFilesToStagingDir.ps1 index 0c6d35ee6a..713f69ae10 100644 --- a/build/CopyFilesToStagingDir.ps1 +++ b/build/CopyFilesToStagingDir.ps1 @@ -34,6 +34,7 @@ function PublishFile { } PublishFile $OverrideDir\DynamicDependency-Override.json $FullPublishDir\ +PublishFile $OverrideDir\PushNotifications-Override.json $FullPublishDir\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.WindowsAppRuntime.dll $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.WindowsAppRuntime.lib $FullPublishDir\Microsoft.WindowsAppRuntime\ @@ -87,6 +88,9 @@ PublishFile $FullBuildOutput\Microsoft.Windows.System.Power.Projection\Microsoft # Dynamic Dependency build overrides PublishFile $OverrideDir\DynamicDependency-Override.json $NugetDir\runtimes\win10-$Platform\native # +# Push Notifications build overrides +PublishFile $OverrideDir\PushNotifications-Override.json $NugetDir\runtimes\win10-$Platform\native +# # Includes (*.h) PublishFile $FullBuildOutput\WindowsAppRuntime_BootstrapDLL\MddBootstrap.h $NugetDir\include PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\MsixDynamicDependency.h $NugetDir\include @@ -104,11 +108,17 @@ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.WindowsAppRuntime.p # MSIX Main package PublishFile $FullBuildOutput\DynamicDependency.DataStore\DynamicDependency.DataStore.exe $NugetDir\runtimes\win10-$Platform\native PublishFile $FullBuildOutput\DynamicDependency.DataStore\DynamicDependency.DataStore.pdb $NugetDir\runtimes\win10-$Platform\native -PublishFile $FullBuildOutput\PushNotificationsLongRunningTask\PushNotificationsLongRunningTask.exe $NugetDir\runtimes\win10-$Platform\native -PublishFile $FullBuildOutput\PushNotificationsLongRunningTask\PushNotificationsLongRunningTask.pdb $NugetDir\runtimes\win10-$Platform\native PublishFile $FullBuildOutput\DynamicDependency.DataStore.ProxyStub\DynamicDependency.DataStore.ProxyStub.dll $NugetDir\runtimes\win10-$Platform\native PublishFile $FullBuildOutput\DynamicDependency.DataStore.ProxyStub\DynamicDependency.DataStore.ProxyStub.pdb $NugetDir\runtimes\win10-$Platform\native # +# PushNotifications LRP +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask\PushNotificationsLongRunningTask.exe $NugetDir\runtimes\win10-$Platform\native +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask\PushNotificationsLongRunningTask.pdb $NugetDir\runtimes\win10-$Platform\native +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask.StartupTask\PushNotificationsLongRunningTask.StartupTask.exe $NugetDir\runtimes\win10-$Platform\native +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask.StartupTask\PushNotificationsLongRunningTask.StartupTask.pdb $NugetDir\runtimes\win10-$Platform\native +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask.ProxyStub\PushNotificationsLongRunningTask.ProxyStub.dll $NugetDir\runtimes\win10-$Platform\native +PublishFile $FullBuildOutput\PushNotificationsLongRunningTask.ProxyStub\PushNotificationsLongRunningTask.ProxyStub.pdb $NugetDir\runtimes\win10-$Platform\native +# # MSIX DDLM package PublishFile $FullBuildOutput\DynamicDependencyLifetimeManager\DynamicDependencyLifetimeManager.exe $NugetDir\runtimes\win10-$Platform\native PublishFile $FullBuildOutput\DynamicDependencyLifetimeManager\DynamicDependencyLifetimeManager.pdb $NugetDir\runtimes\win10-$Platform\native @@ -129,3 +139,7 @@ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.Windows.Application PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.Windows.ApplicationModel.WindowsAppRuntime.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.Windows.System.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\Microsoft.Windows.System.Power.winmd $NugetDir\lib\uap10.0 +# +# Build overrides +PublishFile $OverrideDir\DynamicDependency-Override.json $NugetDir\runtimes\win10-$Platform\native +PublishFile $OverrideDir\PushNotifications-Override.json $NugetDir\runtimes\win10-$Platform\native \ No newline at end of file diff --git a/dev/AppLifecycle/ActivationRegistrationManager.cpp b/dev/AppLifecycle/ActivationRegistrationManager.cpp index 2fa802efc3..e6889722c0 100644 --- a/dev/AppLifecycle/ActivationRegistrationManager.cpp +++ b/dev/AppLifecycle/ActivationRegistrationManager.cpp @@ -21,7 +21,7 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation // Example: C:\some\path\App.exe "----ms-protocol:myscheme:some=data&some=other" return wil::str_printf(L"%s \"%s%s%s%s\"", exePath.c_str(), c_argumentPrefix, - c_protocolArgumentString, c_argumentSuffix, argumentData.c_str()); + c_msProtocolArgumentString, c_argumentSuffix, argumentData.c_str()); } void ActivationRegistrationManager::RegisterForFileTypeActivation( diff --git a/dev/AppLifecycle/ActivationRegistrationManager.h b/dev/AppLifecycle/ActivationRegistrationManager.h index c675a5e285..b8b6fc8826 100644 --- a/dev/AppLifecycle/ActivationRegistrationManager.h +++ b/dev/AppLifecycle/ActivationRegistrationManager.h @@ -9,7 +9,8 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation // Registration constant values. static PCWSTR c_argumentPrefix{ L"----" }; static PCWSTR c_argumentSuffix{ L":" }; - static PCWSTR c_protocolArgumentString{ L"ms-protocol" }; + static PCWSTR c_msProtocolArgumentString{ L"ms-protocol" }; + static PCWSTR c_pushProtocolArgumentString{ L"WindowsAppRuntimePushServer" }; static PCWSTR c_runKeyPath{ LR"(Software\Microsoft\Windows\CurrentVersion\Run\)" }; struct ActivationRegistrationManager diff --git a/dev/AppLifecycle/AppInstance.cpp b/dev/AppLifecycle/AppInstance.cpp index 32a507e112..2abec8eaf9 100644 --- a/dev/AppLifecycle/AppInstance.cpp +++ b/dev/AppLifecycle/AppInstance.cpp @@ -20,19 +20,17 @@ using namespace winrt::Windows::ApplicationModel::Activation; namespace winrt::Microsoft::Windows::AppLifecycle::implementation { + static PCWSTR c_pushPayloadAttribute{ L"-Payload:" }; + INIT_ONCE AppInstance::s_initOnce{}; winrt::com_ptr AppInstance::s_current; - std::tuple ParseCommandLine(const std::wstring& commandLine) + std::tuple GetActivationArguments(PWSTR argv[], int argc, PCWSTR activationKind) { - int argc{}; - wil::unique_hlocal_ptr argv{ CommandLineToArgvW(commandLine.c_str(), &argc) }; - - // Search for ----ms-protocol: for (int index = 0; index < argc; index++) { std::wstring_view fullArgument = argv[index]; - auto protocolQualifier = wil::str_printf(L"%s%s%s", c_argumentPrefix, c_protocolArgumentString, c_argumentSuffix); + auto protocolQualifier = wil::str_printf(L"%s%s%s", c_argumentPrefix, activationKind, c_argumentSuffix); auto argStart = fullArgument.find(protocolQualifier); if (argStart == std::wstring::npos) @@ -55,6 +53,25 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation return { argument.substr(0, argsDelim), argument.substr(argsDelim + 1) }; } + return { L"", L""}; + } + + std::tuple ParseCommandLine(const std::wstring& commandLine) + { + int argc{}; + + wil::unique_hlocal_ptr argv{ CommandLineToArgvW(commandLine.c_str(), &argc) }; + + PCWSTR activationKinds[] = { c_msProtocolArgumentString, c_pushProtocolArgumentString }; + for (auto activationKind : activationKinds) + { + auto [ kind, data ] = GetActivationArguments(argv.get(), argc, activationKind); + if (kind != L"") + { + return { kind, data }; + } + } + return { L"", L"" }; } @@ -341,17 +358,30 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation // protocol, except the catch-all LaunchActivatedEventArgs case. if (!contractArgument.empty()) { - if (contractData.empty()) + if (contractArgument == c_pushProtocolArgumentString) { - // If the contractData is empty, handle any aliased encoded launches. - if (CompareStringOrdinal(contractArgument.data(), static_cast(contractArgument.size()), L"WindowsAppRuntimePushServer", -1, TRUE) == CSTR_EQUAL) + // Generate a basic encoded launch Uri for all Push activations. + std::wstring tempContractData = GenerateEncodedLaunchUri(L"App", c_pushContractId); + contractArgument = c_msProtocolArgumentString; + + // A non-empty contractData means we have a payload. + // This contains a background notification. It is specific to unpackaged apps. + // It requires further processing to build PushNotificationReceivedEventArgs. + // For packaged apps we don't need extra processing. A basic encoded launch Uri is sufficient. + auto index = contractData.find(c_pushPayloadAttribute); + + if (!contractData.empty() && index == 0) { - contractData = GenerateEncodedLaunchUri(L"App", c_pushContractId); - contractArgument = c_protocolArgumentString; + tempContractData += L"&payload="; + // 9 -> the size of &payload= as quotes in the contrat data will + // have been tripped in the call to ParseCommandLine. + tempContractData += contractData.substr(9, contractData.size() - 9); } + + contractData = tempContractData; } - if (CompareStringOrdinal(contractArgument.c_str(), static_cast(contractArgument.size()), c_protocolArgumentString, -1, TRUE) == CSTR_EQUAL) + if (CompareStringOrdinal(contractArgument.c_str(), static_cast(contractArgument.size()), c_msProtocolArgumentString, -1, TRUE) == CSTR_EQUAL) { kind = ExtendedActivationKind::Protocol; auto args = make(contractData.c_str()); diff --git a/dev/PushNotifications/GetRawNotificationEventArgs.h b/dev/PushNotifications/GetRawNotificationEventArgs.h index 66cfaf27a5..2bf1bdd683 100644 --- a/dev/PushNotifications/GetRawNotificationEventArgs.h +++ b/dev/PushNotifications/GetRawNotificationEventArgs.h @@ -10,8 +10,22 @@ constexpr PCWSTR c_pushContractId = L"Windows.Push"; namespace winrt::Microsoft::Windows::PushNotifications { - static winrt::Windows::Foundation::IInspectable Deserialize(winrt::Windows::Foundation::Uri const&) + static winrt::Windows::Foundation::IInspectable Deserialize(winrt::Windows::Foundation::Uri const& uri) { + // Verify if the uri contains a background notification payload. + // Otherwise, we expect to process the notification in a background task. + for (auto const& pair : uri.QueryParsed()) + { + if (pair.Name() == L"payload") + { + // Convert escaped components to its normal content + // from the conversion done in the LRP (see NotificationListener.cpp) + std::wstring payloadAsEscapedWstring{ pair.Value() }; + std::wstring payloadAsWstring{ winrt::Windows::Foundation::Uri::UnescapeComponent(payloadAsEscapedWstring) }; + return winrt::make(payloadAsWstring); + } + } + const DWORD receiveArgsTimeoutInMSec{ 2000 }; THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_TIMEOUT), !GetWaitHandleForArgs().wait(receiveArgsTimeoutInMSec)); diff --git a/dev/PushNotifications/PushNotificationChannel.cpp b/dev/PushNotifications/PushNotificationChannel.cpp index d592a745c4..73c5395eee 100644 --- a/dev/PushNotifications/PushNotificationChannel.cpp +++ b/dev/PushNotifications/PushNotificationChannel.cpp @@ -2,18 +2,23 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. #include "pch.h" +#include #include "PushNotificationChannel.h" #include "Microsoft.Windows.PushNotifications.PushNotificationChannel.g.cpp" #include #include #include "PushNotificationReceivedEventArgs.h" +#include +#include "externs.h" #include "PushNotificationTelemetry.h" #include +#include "PushNotificationUtility.h" namespace winrt::Windows { using namespace winrt::Windows::Networking::PushNotifications; using namespace winrt::Windows::Foundation; + using namespace winrt::Windows::Metadata; } namespace winrt::Microsoft { @@ -29,17 +34,40 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation winrt::Windows::Uri PushNotificationChannel::Uri() { - return winrt::Windows::Uri{ m_channel.Uri() }; + if (m_channel) + { + return winrt::Windows::Uri{ m_channel.Uri() }; + } + else + { + return winrt::Windows::Uri{ m_channelInfo.channelUri }; + } } + winrt::Windows::DateTime PushNotificationChannel::ExpirationTime() { - return m_channel.ExpirationTime(); + if (m_channel) + { + return m_channel.ExpirationTime(); + } + else + { + return m_channelInfo.channelExpiryTime; + } } + void PushNotificationChannel::Close() { try { - m_channel.Close(); + if (m_channel) + { + m_channel.Close(); + } + else + { + THROW_IF_FAILED(PushNotifications_CloseChannel(m_channelInfo.appUserModelId.c_str(), m_channelInfo.channelId.c_str())); + } PushNotificationTelemetry::ChannelClosedByApi(S_OK); } @@ -59,18 +87,125 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation winrt::event_token PushNotificationChannel::PushReceived(winrt::Windows::TypedEventHandler handler) { - return m_channel.PushNotificationReceived([weak_self = get_weak(), handler](auto&&, auto&& args) + if (IsPackagedAppScenario()) + { + if (m_channel) + { + return m_channel.PushNotificationReceived([weak_self = get_weak(), handler](auto&&, auto&& args) + { + if (auto strong = weak_self.get()) + { + auto pushArgs = winrt::make(args); + pushArgs.Handled(true); + handler(*strong, pushArgs); + }; + }); + } + else + { + // The channelUri is directly obtained when we request Channel from UDK using RemoteId + auto lock = m_lock.lock_exclusive(); + + if (!m_foregroundHandlerCount) + { + auto appUserModelId{ winrt::Microsoft::Helpers::GetAppUserModelId() }; + + THROW_IF_FAILED(PushNotifications_RegisterNotificationSinkForFullTrustApplication(appUserModelId.get(), this)); + } + + ++m_foregroundHandlerCount; + + return m_foregroundHandlers.add(handler); + } + } + else { - auto strong = weak_self.get(); - if (strong) + auto lock = m_lock.lock_exclusive(); + if (!m_foregroundHandlerCount++) { - handler(*strong, winrt::make(args)); - }; - }); + auto notificationsLongRunningPlatform{ winrt::Microsoft::Helpers::GetNotificationPlatform() }; + + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + + THROW_IF_FAILED(notificationsLongRunningPlatform->RegisterForegroundActivator(this, processName.get())); + } + return m_foregroundHandlers.add(handler); + } } void PushNotificationChannel::PushReceived(winrt::event_token const& token) noexcept { - m_channel.PushNotificationReceived(token); + if (IsPackagedAppScenario()) + { + if (m_channel) + { + m_channel.PushNotificationReceived(token); + } + else + { + auto lock = m_lock.lock_exclusive(); + + if (m_foregroundHandlerCount == 1) + { + auto appUserModelId{ winrt::Microsoft::Helpers::GetAppUserModelId() }; + + THROW_IF_FAILED(PushNotifications_UnregisterNotificationSinkForFullTrustApplication(appUserModelId.get())); + } + + m_foregroundHandlers.remove(token); + --m_foregroundHandlerCount; + } + } + else + { + auto lock = m_lock.lock_exclusive(); + m_foregroundHandlers.remove(token); + if (!--m_foregroundHandlerCount) + { + auto notificationsLongRunningPlatform{ winrt::Microsoft::Helpers::GetNotificationPlatform() }; + + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + + THROW_IF_FAILED(notificationsLongRunningPlatform->UnregisterForegroundActivator(processName.get())); + } + } + } + + HRESULT __stdcall PushNotificationChannel::InvokeAll(_In_ ULONG length, _In_ byte* payload, _Out_ BOOL* foregroundHandled) noexcept try + { + auto args = winrt::make(payload, length); + m_foregroundHandlers(*this, args); + *foregroundHandled = args.Handled(); + return S_OK; + } + CATCH_RETURN() + + HRESULT __stdcall PushNotificationChannel::OnRawNotificationReceived(unsigned int payloadLength, _In_ byte* payload, _In_ HSTRING /*correlationVector */) noexcept try + { + BOOL foregroundHandled = true; + THROW_IF_FAILED(InvokeAll(payloadLength, payload, &foregroundHandled)); + + if (!foregroundHandled) + { + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + THROW_IF_FAILED(winrt::Microsoft::Helpers::ProtocolLaunchHelper(processName.get(), payloadLength, payload)); + } + + return S_OK; + } + CATCH_RETURN(); + + bool PushNotificationChannel::IsBackgroundTaskBuilderAvailable() + { + return winrt::Windows::ApiInformation::IsMethodPresent(L"Windows.ApplicationModel.Background.BackgroundTaskBuilder", L"SetTaskEntryPointClsid"); + } + + // Determines if the caller should be treated as packaged app or not. + bool PushNotificationChannel::IsPackagedAppScenario() + { + return AppModel::Identity::IsPackagedProcess() && IsBackgroundTaskBuilderAvailable(); } } diff --git a/dev/PushNotifications/PushNotificationChannel.h b/dev/PushNotifications/PushNotificationChannel.h index 8349d8f6b8..1a1595acd1 100644 --- a/dev/PushNotifications/PushNotificationChannel.h +++ b/dev/PushNotifications/PushNotificationChannel.h @@ -3,12 +3,23 @@ #pragma once #include "Microsoft.Windows.PushNotifications.PushNotificationChannel.g.h" +#include +#include "winrt/Windows.Networking.PushNotifications.h" +#include +#include "externs.h" namespace winrt::Microsoft::Windows::PushNotifications::implementation { - struct PushNotificationChannel : PushNotificationChannelT + typedef winrt::Windows::Foundation::TypedEventHandler< + winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel, + winrt::Microsoft::Windows::PushNotifications::PushNotificationReceivedEventArgs> PushNotificationEventHandler; + + struct PushNotificationChannel : PushNotificationChannelT { PushNotificationChannel(winrt::Windows::Networking::PushNotifications::PushNotificationChannel const& channel); + + PushNotificationChannel(struct ChannelDetails const& channelInfo) : m_channelInfo(channelInfo) {}; + winrt::Windows::Foundation::Uri Uri(); winrt::Windows::Foundation::DateTime ExpirationTime(); void Close(); @@ -16,14 +27,21 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation winrt::event_token PushReceived(winrt::Windows::Foundation::TypedEventHandler handler); void PushReceived(winrt::event_token const& token) noexcept; + // IWpnForegroundSink + HRESULT __stdcall InvokeAll(_In_ ULONG length, _In_ byte* payload, _Out_ BOOL* foregroundHandled) noexcept; + + // INotificationHandler + HRESULT __stdcall OnRawNotificationReceived(unsigned int payloadLength, _In_ byte* payload, _In_ HSTRING /*correlationVector */) noexcept; + private: + bool IsPackagedAppScenario(); + bool IsBackgroundTaskBuilderAvailable(); + const winrt::Windows::Networking::PushNotifications::PushNotificationChannel m_channel{ nullptr }; + const struct ChannelDetails m_channelInfo{}; - }; -} -namespace winrt::Microsoft::Windows::PushNotifications::factory_implementation -{ - struct PushNotificationChannel : PushNotificationChannelT - { + winrt::event m_foregroundHandlers; + ULONG m_foregroundHandlerCount = 0; + wil::srwlock m_lock; }; } diff --git a/dev/PushNotifications/PushNotificationDummyDeferral.h b/dev/PushNotifications/PushNotificationDummyDeferral.h new file mode 100644 index 0000000000..75e2ab06e8 --- /dev/null +++ b/dev/PushNotifications/PushNotificationDummyDeferral.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once +#include "pch.h" +#include +#include + +struct PushNotificationDummyDeferral : winrt::implements +{ + PushNotificationDummyDeferral() {} + + void Complete() { }; +}; + +struct PushNotificationDummyDeferralFactory : winrt::implements +{ + HRESULT __stdcall CreateInstance(_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final try + { + THROW_HR_IF(CLASS_E_NOAGGREGATION, aggregateInterface != nullptr); + return winrt::make().as(interfaceId, object); + } + CATCH_RETURN() + + HRESULT __stdcall LockServer(BOOL) noexcept final + { + return S_OK; + } +}; diff --git a/dev/PushNotifications/PushNotificationManager.cpp b/dev/PushNotifications/PushNotificationManager.cpp index a81282052e..44d207e3ac 100644 --- a/dev/PushNotifications/PushNotificationManager.cpp +++ b/dev/PushNotifications/PushNotificationManager.cpp @@ -17,7 +17,10 @@ #include "PushNotificationChannel.h" #include "externs.h" #include +#include +#include "NotificationsLongRunningProcess_h.h" #include +#include "PushNotificationUtility.h" using namespace std::literals; @@ -37,10 +40,15 @@ namespace winrt using namespace Windows::Foundation; } +namespace PushNotificationHelpers +{ + using namespace winrt::Microsoft::Windows::PushNotifications::Helpers; +} namespace winrt::Microsoft::Windows::PushNotifications::implementation { static winrt::Windows::ApplicationModel::Background::IBackgroundTaskRegistration s_pushTriggerRegistration{ nullptr }; static wil::unique_com_class_object_cookie s_comActivatorRegistration; + static bool s_protocolRegistration{ false }; static wil::srwlock s_activatorInfoLock; inline constexpr auto c_maxBackoff{ 5min }; @@ -51,7 +59,7 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation const HRESULT WNP_E_RECONNECTING = static_cast(0x880403E9L); const HRESULT WNP_E_BIND_USER_BUSY = static_cast(0x880403FEL); - bool PushNotificationManager::IsChannelRequestRetryable(const hresult& hr) + bool IsChannelRequestRetryable(const hresult& hr) { switch (hr) { @@ -67,17 +75,63 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation } } - winrt::IAsyncOperationWithProgress PushNotificationManager::CreateChannelAsync(winrt::guid const& remoteId) + void RegisterUnpackagedApplicationHelper( + const winrt::guid& remoteId, + _Out_ wil::unique_cotaskmem_string &unpackagedAppUserModelId) + { + auto coInitialize = wil::CoInitializeEx(); + + auto notificationPlatform{ PushNotificationHelpers::GetNotificationPlatform() }; + + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + + THROW_IF_FAILED(notificationPlatform->RegisterFullTrustApplication(processName.get(), remoteId, &unpackagedAppUserModelId)); + } + + winrt::hresult CreateChannelWithRemoteIdHelper(const winrt::guid& remoteId, ChannelDetails& channelInfo) noexcept try + { + wchar_t appUserModelId[APPLICATION_USER_MODEL_ID_MAX_LENGTH] = {}; + UINT32 appUserModelIdSize{ ARRAYSIZE(appUserModelId) }; + + THROW_IF_FAILED(GetCurrentApplicationUserModelId(&appUserModelIdSize, appUserModelId)); + + THROW_HR_IF(E_INVALIDARG, (appUserModelIdSize > APPLICATION_USER_MODEL_ID_MAX_LENGTH) || (appUserModelIdSize == 0)); + + HRESULT operationalCode{}; + ABI::Windows::Foundation::DateTime channelExpiryTime{}; + wil::unique_cotaskmem_string channelId; + wil::unique_cotaskmem_string channelUri; + + THROW_IF_FAILED(PushNotifications_CreateChannelWithRemoteIdentifier( + appUserModelId, + remoteId, + &operationalCode, + &channelId, + &channelUri, + &channelExpiryTime)); + + THROW_IF_FAILED(operationalCode); + + winrt::copy_from_abi(channelInfo.channelExpiryTime, &channelExpiryTime); + channelInfo.appUserModelId = winrt::hstring{ appUserModelId }; + channelInfo.channelId = winrt::hstring{ channelId.get() }; + channelInfo.channelUri = winrt::hstring{ channelUri.get() }; + + return S_OK; + } + CATCH_RETURN() + + winrt::IAsyncOperationWithProgress PushNotificationManager::CreateChannelAsync(const winrt::guid remoteId) { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::PushNotifications::Feature_PushNotifications::IsEnabled()); + bool usingLegacyImplementation{ false }; + try { THROW_HR_IF(E_INVALIDARG, (remoteId == winrt::guid())); - // API supports channel requests only for packaged applications for v0.8 version - THROW_HR_IF(E_NOTIMPL, !AppModel::Identity::IsPackagedProcess()); - auto cancellation{ co_await winrt::get_cancellation_token() }; cancellation.enable_propagation(true); @@ -100,21 +154,65 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation { try { - PushNotificationChannelManager channelManager{}; - winrt::PushNotificationChannel pushChannelReceived{ nullptr }; + if (IsActivatorSupported(PushNotificationRegistrationActivators::PushTrigger)) + { + ChannelDetails channelInfo{}; + winrt::hresult hr = CreateChannelWithRemoteIdHelper(remoteId, channelInfo); + + // RemoteId APIs are not applicable for downlevel OS versions. + // So we get error E_NOTIMPL and we fallback to calling into + // public WinRT API CreatePushNotificationChannelForApplicationAsync + // to request a channel. + if (SUCCEEDED(hr)) + { + PushNotificationTelemetry::ChannelRequestedByApi(S_OK, remoteId, usingLegacyImplementation); + + co_return winrt::make( + winrt::make(channelInfo), + S_OK, + PushNotificationChannelStatus::CompletedSuccess); + } + else if (hr == E_NOTIMPL) + { + usingLegacyImplementation = true; + + PushNotificationChannelManager channelManager{}; + winrt::PushNotificationChannel pushChannelReceived{ nullptr }; + + pushChannelReceived = co_await channelManager.CreatePushNotificationChannelForApplicationAsync(); + + PushNotificationTelemetry::ChannelRequestedByApi(S_OK, remoteId, usingLegacyImplementation); + + co_return winrt::make( + winrt::make(pushChannelReceived), + S_OK, + PushNotificationChannelStatus::CompletedSuccess); + } + else + { + winrt::check_hresult(hr); + } + } + else + { + wil::unique_cotaskmem_string unpackagedAppUserModelId; + RegisterUnpackagedApplicationHelper(remoteId, unpackagedAppUserModelId); + PushNotificationChannelManager channelManager{}; + winrt::PushNotificationChannel pushChannelReceived{ co_await channelManager.CreatePushNotificationChannelForApplicationAsync(unpackagedAppUserModelId.get()) }; - pushChannelReceived = co_await channelManager.CreatePushNotificationChannelForApplicationAsync(); + auto notificationPlatform{ PushNotificationHelpers::GetNotificationPlatform() }; - PushNotificationTelemetry::ChannelRequestedByApi( - S_OK, - AppModel::Identity::IsPackagedProcess(), - remoteId); + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + THROW_IF_FAILED(notificationPlatform->RegisterLongRunningActivator(processName.get())); - co_return winrt::make( - winrt::make(pushChannelReceived), - S_OK, - PushNotificationChannelStatus::CompletedSuccess); + PushNotificationTelemetry::ChannelRequestedByApi(S_OK, remoteId, usingLegacyImplementation); + co_return winrt::make( + winrt::make(pushChannelReceived), + S_OK, + PushNotificationChannelStatus::CompletedSuccess); + } } catch (...) { @@ -130,11 +228,7 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation } else { - - PushNotificationTelemetry::ChannelRequestedByApi( - channelRequestException.code(), - AppModel::Identity::IsPackagedProcess(), - remoteId); + PushNotificationTelemetry::ChannelRequestedByApi(channelRequestException.code(), remoteId, usingLegacyImplementation); co_return winrt::make( nullptr, @@ -148,13 +242,8 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation } catch (...) { - HRESULT hrError = wil::ResultFromCaughtException(); - PushNotificationTelemetry::ChannelRequestedByApi( - hrError, - AppModel::Identity::IsPackagedProcess(), - remoteId); - - THROW_HR(hrError); + PushNotificationTelemetry::ChannelRequestedByApi(wil::ResultFromCaughtException(), remoteId, usingLegacyImplementation); + throw; } } @@ -166,18 +255,39 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation { THROW_HR_IF_NULL(E_INVALIDARG, details); - GUID taskClsid = details.TaskClsid(); - THROW_HR_IF(E_INVALIDARG, taskClsid == GUID_NULL); - auto registrationActivators{ details.Activators() }; - THROW_HR_IF(E_INVALIDARG, WI_AreAllFlagsClear(registrationActivators, PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator)); + + auto isBackgroundTaskFlagSet{ WI_IsAnyFlagSet(registrationActivators, PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator) }; + THROW_HR_IF(E_INVALIDARG, isBackgroundTaskFlagSet && !IsActivatorSupported(registrationActivators)); + + auto isProtocolActivatorSet{ WI_IsFlagSet(registrationActivators, PushNotificationRegistrationActivators::ProtocolActivator) }; + THROW_HR_IF(E_INVALIDARG, isProtocolActivatorSet && !IsActivatorSupported(registrationActivators)); + + if (isProtocolActivatorSet) + { + { + auto lock = s_activatorInfoLock.lock_shared(); + THROW_HR_IF(E_INVALIDARG, s_protocolRegistration); + } + + wil::unique_cotaskmem_string unpackagedAppUserModelId; + RegisterUnpackagedApplicationHelper(GUID_NULL, unpackagedAppUserModelId); // create default registration for app + + { + auto lock = s_activatorInfoLock.lock_exclusive(); + s_protocolRegistration = true; + } + } BackgroundTaskBuilder builder{ nullptr }; if (WI_IsFlagSet(registrationActivators, PushNotificationRegistrationActivators::PushTrigger)) { + GUID taskClsid = details.TaskClsid(); + THROW_HR_IF(E_INVALIDARG, taskClsid == GUID_NULL); + { - auto lock = s_activatorInfoLock.lock_exclusive(); + auto lock = s_activatorInfoLock.lock_shared(); THROW_HR_IF(E_INVALIDARG, s_pushTriggerRegistration); } @@ -242,8 +352,11 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation if (WI_IsFlagSet(registrationActivators, PushNotificationRegistrationActivators::ComActivator)) { + GUID taskClsid = details.TaskClsid(); + THROW_HR_IF(E_INVALIDARG, taskClsid == GUID_NULL); + { - auto lock = s_activatorInfoLock.lock_exclusive(); + auto lock = s_activatorInfoLock.lock_shared(); THROW_HR_IF_MSG(E_INVALIDARG, s_comActivatorRegistration, "ComActivator already registered."); } @@ -272,11 +385,9 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation catch(...) { - HRESULT hrError = wil::ResultFromCaughtException(); - PushNotificationTelemetry::ActivatorRegisteredByApi(hrError, + PushNotificationTelemetry::ActivatorRegisteredByApi(wil::ResultFromCaughtException(), details == nullptr ? PushNotificationRegistrationActivators::Undefined : details.Activators()); - - THROW_HR(hrError); + throw; } } @@ -300,12 +411,25 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !s_comActivatorRegistration, "ComActivator not registered."); s_comActivatorRegistration.reset(); } + + if (WI_IsFlagSet(activators, PushNotificationRegistrationActivators::ProtocolActivator)) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !s_protocolRegistration); + auto coInitialize = wil::CoInitializeEx(); + + auto notificationPlatform{ PushNotificationHelpers::GetNotificationPlatform() }; + + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + LOG_IF_FAILED(notificationPlatform->UnregisterLongRunningActivator(processName.get())); + + s_protocolRegistration = false; + } } catch (...) { - HRESULT hrError = wil::ResultFromCaughtException(); - PushNotificationTelemetry::ActivatorUnregisteredByApi(hrError, activators); - THROW_HR(hrError); + PushNotificationTelemetry::ActivatorUnregisteredByApi(wil::ResultFromCaughtException(), activators); + throw; } PushNotificationTelemetry::ActivatorUnregisteredByApi(S_OK, activators); @@ -325,12 +449,25 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation } s_comActivatorRegistration.reset(); + + if (s_protocolRegistration) + { + auto coInitialize = wil::CoInitializeEx(); + + auto notificationPlatform{ PushNotificationHelpers::GetNotificationPlatform() }; + + wil::unique_cotaskmem_string processName; + THROW_IF_FAILED(GetCurrentProcessPath(processName)); + LOG_IF_FAILED(notificationPlatform->UnregisterLongRunningActivator(processName.get())); + + s_protocolRegistration = false; + } } catch(...) { - HRESULT hrError = wil::ResultFromCaughtException(); - PushNotificationTelemetry::ActivatorUnregisteredByApi(hrError, PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); - THROW_HR(hrError); + PushNotificationTelemetry::ActivatorUnregisteredByApi(wil::ResultFromCaughtException(), + PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); + throw; } PushNotificationTelemetry::ActivatorUnregisteredByApi(S_OK, PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); } @@ -339,7 +476,7 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation return Metadata::ApiInformation::IsMethodPresent(L"Windows.ApplicationModel.Background.BackgroundTaskBuilder", L"SetTaskEntryPointClsid"); } - bool PushNotificationManager::IsBackgroundTaskBuilderAvailable() + bool IsBackgroundTaskBuilderAvailable() { static bool hasSetTaskEntrypoint = HasBackgroundTaskEntryPointClsid(); return hasSetTaskEntrypoint; diff --git a/dev/PushNotifications/PushNotificationManager.h b/dev/PushNotifications/PushNotificationManager.h index c32c9db217..56c1f537d9 100644 --- a/dev/PushNotifications/PushNotificationManager.h +++ b/dev/PushNotifications/PushNotificationManager.h @@ -3,6 +3,7 @@ #pragma once #include "Microsoft.Windows.PushNotifications.PushNotificationManager.g.h" +#include namespace winrt::Microsoft::Windows::PushNotifications::implementation { @@ -15,13 +16,9 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation static void UnregisterActivator(Microsoft::Windows::PushNotifications::PushNotificationRegistrationActivators const& activators); static void UnregisterAllActivators(); - static winrt::Windows::Foundation::IAsyncOperationWithProgress CreateChannelAsync(winrt::guid const& remoteId); + static winrt::Windows::Foundation::IAsyncOperationWithProgress CreateChannelAsync(winrt::guid const remoteId); static bool IsActivatorSupported(Microsoft::Windows::PushNotifications::PushNotificationRegistrationActivators const& activators); - - private: - static bool IsChannelRequestRetryable(const winrt::hresult& hrException); - static bool IsBackgroundTaskBuilderAvailable(); }; } namespace winrt::Microsoft::Windows::PushNotifications::factory_implementation diff --git a/dev/PushNotifications/PushNotificationReceivedEventArgs.cpp b/dev/PushNotifications/PushNotificationReceivedEventArgs.cpp index 1ede2928fe..94877d115a 100644 --- a/dev/PushNotifications/PushNotificationReceivedEventArgs.cpp +++ b/dev/PushNotifications/PushNotificationReceivedEventArgs.cpp @@ -10,8 +10,11 @@ #include "PushNotificationReceivedEventArgs.h" #include "Microsoft.Windows.PushNotifications.PushNotificationReceivedEventArgs.g.cpp" #include +#include #include +#include #include "ValueMarshaling.h" +#include "PushNotificationUtility.h" namespace winrt { @@ -23,55 +26,122 @@ namespace winrt namespace winrt::Microsoft::Windows::PushNotifications::implementation { - PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::IBackgroundTaskInstance const& backgroundTask): m_backgroundTaskInstance(backgroundTask), m_rawNotification(backgroundTask.TriggerDetails().as().ContentBytes()) + PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::IBackgroundTaskInstance const& backgroundTask): + m_backgroundTaskInstance(backgroundTask), + m_rawNotificationPayload(BuildPayload(backgroundTask.TriggerDetails().as().ContentBytes())), + m_unpackagedAppScenario(false) { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::PushNotifications::Feature_PushNotifications::IsEnabled()); } - PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::PushNotificationReceivedEventArgs const& args): m_args(args), m_rawNotification(args.RawNotification().ContentBytes()) + PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::PushNotificationReceivedEventArgs const& args): + m_args(args), + m_rawNotificationPayload(BuildPayload(args.RawNotification().ContentBytes())), + m_unpackagedAppScenario(false) { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::PushNotifications::Feature_PushNotifications::IsEnabled()); } + PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(byte* const& payload, ULONG const& length) : + m_rawNotificationPayload(BuildPayload(payload, length)), + m_unpackagedAppScenario(true) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::PushNotifications::Feature_PushNotifications::IsEnabled()); + } + + PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(std::wstring const& payload) : + m_rawNotificationPayload(BuildPayload(payload)), + m_unpackagedAppScenario(true) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::PushNotifications::Feature_PushNotifications::IsEnabled()); + } + + std::vector PushNotificationReceivedEventArgs::BuildPayload(winrt::Windows::Storage::Streams::IBuffer const& buffer) + { + return { buffer.data(), buffer.data() + (buffer.Length() * sizeof(uint8_t)) }; + } + + std::vector PushNotificationReceivedEventArgs::BuildPayload(byte* const& payload, ULONG const& length) + { + return { payload, payload + (length * sizeof(uint8_t)) }; + } + + std::vector PushNotificationReceivedEventArgs::BuildPayload(std::wstring const& payload) + { + std::string payloadToSimpleString{ ::winrt::Microsoft::Windows::PushNotifications::Helpers::WideStringToUtf8String(payload) }; + return { payloadToSimpleString.c_str(), payloadToSimpleString.c_str() + (payloadToSimpleString.length() * sizeof(uint8_t)) }; + } + winrt::com_array PushNotificationReceivedEventArgs::Payload() { - auto rawNotificationData = m_rawNotification.data(); - return { rawNotificationData, rawNotificationData + (m_rawNotification.Length() * sizeof(uint8_t)) }; + return { m_rawNotificationPayload.data(), m_rawNotificationPayload.data() + (m_rawNotificationPayload.size() * sizeof(uint8_t)) }; } winrt::BackgroundTaskDeferral PushNotificationReceivedEventArgs::GetDeferral() { - THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); - - return m_backgroundTaskInstance.GetDeferral(); + if (!m_unpackagedAppScenario) + { + THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); + + return m_backgroundTaskInstance.GetDeferral(); + } + else + { + auto dummyDeferral = winrt::make(); + return dummyDeferral.as(); + } } winrt::event_token PushNotificationReceivedEventArgs::Canceled(winrt::BackgroundTaskCanceledEventHandler const& handler) { - THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); - - return m_backgroundTaskInstance.Canceled(handler); + if (!m_unpackagedAppScenario) + { + THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); + + return m_backgroundTaskInstance.Canceled(handler); + } + else + { + return { 0 }; + } } void PushNotificationReceivedEventArgs::Canceled(winrt::event_token const& token) noexcept { - THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); + if (!m_unpackagedAppScenario) + { + THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_backgroundTaskInstance, "Foreground activation cannot call this."); - m_backgroundTaskInstance.Canceled(token); + m_backgroundTaskInstance.Canceled(token); + } } bool PushNotificationReceivedEventArgs::Handled() { - THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_args, "Background activation cannot call this."); - - return m_args.Cancel(); + if (!m_unpackagedAppScenario) + { + THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_args, "Background activation cannot call this."); + + return m_args.Cancel(); + } + else + { + return m_handledUnpackaged; + } } void PushNotificationReceivedEventArgs::Handled(bool value) { - THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_args, "Background activation cannot call this."); - - m_args.Cancel(value); + if (!m_unpackagedAppScenario) + { + THROW_HR_IF_NULL_MSG(E_ILLEGAL_METHOD_CALL, m_args, "Background activation cannot call this."); + + m_args.Cancel(value); + } + else + { + m_handledUnpackaged = value; + } } } diff --git a/dev/PushNotifications/PushNotificationReceivedEventArgs.h b/dev/PushNotifications/PushNotificationReceivedEventArgs.h index 017b1f2f08..61b8a7e286 100644 --- a/dev/PushNotifications/PushNotificationReceivedEventArgs.h +++ b/dev/PushNotifications/PushNotificationReceivedEventArgs.h @@ -1,5 +1,7 @@ #pragma once #include "Microsoft.Windows.PushNotifications.PushNotificationReceivedEventArgs.g.h" +#include +#include namespace winrt::Microsoft::Windows::PushNotifications::implementation { @@ -10,6 +12,10 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation PushNotificationReceivedEventArgs(winrt::Windows::ApplicationModel::Background::IBackgroundTaskInstance const& backgroundTask); PushNotificationReceivedEventArgs(winrt::Windows::Networking::PushNotifications::PushNotificationReceivedEventArgs const& args); + PushNotificationReceivedEventArgs(std::wstring const& payload); + + PushNotificationReceivedEventArgs(byte* const& payload, ULONG const& length); + com_array Payload(); winrt::Windows::ApplicationModel::Background::BackgroundTaskDeferral GetDeferral(); winrt::event_token Canceled(winrt::Windows::ApplicationModel::Background::BackgroundTaskCanceledEventHandler const& handler); @@ -19,7 +25,17 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation private: const winrt::Windows::Storage::Streams::IBuffer m_rawNotification{}; + + std::vector BuildPayload(winrt::Windows::Storage::Streams::IBuffer const& buffer); + std::vector BuildPayload(byte* const& payload, ULONG const& length); + std::vector BuildPayload(std::wstring const& payload); + + std::vector m_rawNotificationPayload; + const winrt::Windows::ApplicationModel::Background::IBackgroundTaskInstance m_backgroundTaskInstance{}; const winrt::Windows::Networking::PushNotifications::PushNotificationReceivedEventArgs m_args = nullptr; + + bool m_unpackagedAppScenario; + bool m_handledUnpackaged = true; }; } diff --git a/dev/PushNotifications/PushNotificationTelemetry.h b/dev/PushNotifications/PushNotificationTelemetry.h index bde401b2af..fc62ca3606 100644 --- a/dev/PushNotifications/PushNotificationTelemetry.h +++ b/dev/PushNotifications/PushNotificationTelemetry.h @@ -3,7 +3,7 @@ #pragma once -#include "..\WindowsAppRuntime_Insights\WindowsAppRuntimeInsights.h" +#include #include DECLARE_TRACELOGGING_CLASS(PushNotificationTelemetryProvider, @@ -21,8 +21,8 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider public: DEFINE_EVENT_METHOD(ChannelRequestedByApi)( winrt::hresult hr, - bool isAppPackaged, - const winrt::guid& remoteId) noexcept try + const winrt::guid& remoteId, + bool usingLegacyImplementation) noexcept try { if (c_maxEventLimit >= UpdateLogEventCount()) { @@ -31,9 +31,10 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), _GENERIC_PARTB_FIELDS_ENABLED, TraceLoggingHexUInt32(hr, "OperationResult"), - TraceLoggingBool(isAppPackaged, "IsAppPackaged"), - TraceLoggingWideString(to_hstring(remoteId).data(), "RemoteId"), - TraceLoggingWideString(GetAppUserModelId(), "AppUserModelId")); + TraceLoggingGuid(remoteId, "RemoteId"), + TraceLoggingBool(usingLegacyImplementation, "usingLegacyImplementation"), + TraceLoggingBool(IsPackagedApp(), "IsAppPackaged"), + TraceLoggingWideString(GetAppName(), "AppName")); } } CATCH_LOG() @@ -48,7 +49,8 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), _GENERIC_PARTB_FIELDS_ENABLED, TraceLoggingHexUInt32(hr, "OperationResult"), - TraceLoggingWideString(GetAppUserModelId(), "AppUserModelId")); + TraceLoggingBool(IsPackagedApp(), "IsAppPackaged"), + TraceLoggingWideString(GetAppName(), "AppName")); } } CATCH_LOG() @@ -66,7 +68,8 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider TraceLoggingHexUInt32(hr, "OperationResult"), TraceLoggingHexUInt32(static_cast>(activators), "RegistrationActivators"), - TraceLoggingWideString(GetAppUserModelId(), "AppUserModelId")); + TraceLoggingBool(IsPackagedApp(), "IsAppPackaged"), + TraceLoggingWideString(GetAppName(), "AppName")); } } CATCH_LOG() @@ -85,7 +88,8 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider TraceLoggingHexUInt32(hr, "OperationResult"), TraceLoggingHexUInt32(static_cast>(activators), "RegistrationActivators"), - TraceLoggingWideString(GetAppUserModelId(), "AppUserModelId")); + TraceLoggingBool(IsPackagedApp(), "IsAppPackaged"), + TraceLoggingWideString(GetAppName(), "AppName")); } } CATCH_LOG() @@ -116,21 +120,61 @@ class PushNotificationTelemetry : public wil::TraceLoggingProvider return m_eventCount; } - wchar_t m_appUserModelId[APPLICATION_USER_MODEL_ID_MAX_LENGTH] = {}; + inline bool IsPackagedApp() + { + static const bool isPackagedApp = AppModel::Identity::IsPackagedProcess(); + + return isPackagedApp; + } + + inline const wchar_t* GetAppName() + { + static const std::wstring appName = IsPackagedApp() ? GetAppNamePackaged() : GetAppNameUnpackaged(); + + return appName.c_str(); + } - wchar_t* GetAppUserModelId() + std::wstring GetAppNamePackaged() noexcept { - if (m_appUserModelId[0] == '\0') + wchar_t appUserModelId[APPLICATION_USER_MODEL_ID_MAX_LENGTH]{}; + + UINT32 appUserModelIdSize = ARRAYSIZE(appUserModelId); + auto result = GetCurrentApplicationUserModelId(&appUserModelIdSize, appUserModelId); + if (result != ERROR_SUCCESS) + { + wcscpy_s(appUserModelId, L"AppUserModelId not found"); + LOG_WIN32(result); + } + + return appUserModelId; + } + + PCWSTR CensorFilePath(PCWSTR path) noexcept + { + if (path) + { + path = !PathIsFileSpecW(path) ? PathFindFileNameW(path) : path; + } + + return path; + } + + std::wstring GetAppNameUnpackaged() + { + std::wstring appName; + + wil::unique_cotaskmem_string processName; + auto result = wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr, processName); + if (result == ERROR_SUCCESS) + { + appName = CensorFilePath(processName.get()); + } + else { - UINT32 appUserModelIdSize = ARRAYSIZE(m_appUserModelId); - auto result = GetCurrentApplicationUserModelId(&appUserModelIdSize, m_appUserModelId); - if (result != ERROR_SUCCESS) - { - wcscpy_s(m_appUserModelId, L"AppUserModelId not found"); - LOG_WIN32(result); - } + appName = L"ModuleFileName not found"; + LOG_WIN32(result); } - return m_appUserModelId; + return appName; } }; diff --git a/dev/PushNotifications/PushNotificationUtility.h b/dev/PushNotifications/PushNotificationUtility.h new file mode 100644 index 0000000000..6fd8d95bad --- /dev/null +++ b/dev/PushNotifications/PushNotificationUtility.h @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once +#include "pch.h" +#include "NotificationsLongRunningProcess_h.h" + +namespace winrt::Microsoft::Windows::PushNotifications::Helpers +{ + inline std::string WideStringToUtf8String(_In_ std::wstring const& utf16string) + { + int size = WideCharToMultiByte( + CP_UTF8, + 0, + utf16string.data(), + static_cast(utf16string.length()), + nullptr, + 0, + nullptr, + nullptr); + + THROW_LAST_ERROR_IF(size == 0); + + std::string utf8string; + utf8string.resize(size); + + size = WideCharToMultiByte( + CP_UTF8, + 0, + utf16string.data(), + static_cast(utf16string.length()), + &utf8string[0], + size, + nullptr, + nullptr); + + THROW_LAST_ERROR_IF(size == 0); + + return utf8string; + } + + inline std::wstring Utf8BytesToWideString(unsigned int payloadLength, _In_reads_(payloadLength) byte* payload) + { + int size = MultiByteToWideChar( + CP_UTF8, + 0, + reinterpret_cast(payload), + payloadLength, + nullptr, + 0); + THROW_LAST_ERROR_IF(size == 0); + + std::wstring payloadAsWideString(size, 0); + size = MultiByteToWideChar( + CP_UTF8, + 0, + reinterpret_cast(payload), + payloadLength, + &payloadAsWideString[0], + size); + THROW_LAST_ERROR_IF(size == 0); + + return payloadAsWideString; + } + + inline wil::unique_cotaskmem_string GetAppUserModelId() + { + wchar_t appId[APPLICATION_USER_MODEL_ID_MAX_LENGTH] = {}; + UINT32 appIdSize{ ARRAYSIZE(appId) }; + + THROW_IF_FAILED(::GetCurrentApplicationUserModelId(&appIdSize, appId)); + + return wil::make_unique_string(appId); + } + + inline HRESULT ProtocolLaunchHelper(std::wstring processName, unsigned int payloadLength, _In_reads_(payloadLength) byte* payload) noexcept try + { + // Command line format: ----WindowsAppRuntimePushServer:-Payload:"" + std::wstring commandLine = L"----WindowsAppRuntimePushServer:-Payload:\""; + + // Escape special characters to follow command line standards for any app activation type in AppLifecycle + // (See AppInstance.cpp and Serialize() from other activation types) + auto payloadAsWideString{ Utf8BytesToWideString(payloadLength, payload) }; + auto payloadAsEscapedUriFormat = winrt::Windows::Foundation::Uri::EscapeComponent(payloadAsWideString.c_str()); + + commandLine.append(payloadAsEscapedUriFormat); + commandLine.append(L"\""); + + SHELLEXECUTEINFO shellExecuteInfo{}; + shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO); + shellExecuteInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_DOENVSUBST; + shellExecuteInfo.lpFile = processName.c_str(); + shellExecuteInfo.lpParameters = commandLine.c_str(); + + shellExecuteInfo.nShow = SW_NORMAL; + + if (!ShellExecuteEx(&shellExecuteInfo)) + { + THROW_IF_WIN32_ERROR(GetLastError()); + } + return S_OK; + } + CATCH_RETURN() + + inline wil::com_ptr GetNotificationPlatform() + { + return wil::CoCreateInstance(CLSCTX_LOCAL_SERVER); + } +} diff --git a/dev/PushNotifications/PushNotifications-Constants.h b/dev/PushNotifications/PushNotifications-Constants.h index ac2ccbe47d..fad7d21336 100644 --- a/dev/PushNotifications/PushNotifications-Constants.h +++ b/dev/PushNotifications/PushNotifications-Constants.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. // Rely on _STRINGIZE(x) in yvals_core.h @@ -12,8 +12,9 @@ #define PUSHNOTIFICATIONS_IMPL_CLSID_UUID E739C755-0D09-48DF-A468-A5DF0B5422DC #define PUSHNOTIFICATIONS_IMPL_CLSID_STRING _STRINGIZE(PUSHNOTIFICATIONS_IMPL_CLSID_UUID) -#define PUSHNOTIFICATIONS_TASK_CLSID_UUID 8FCFB82B-DB93-4E1D-9008-76D768CEB9EA -#define PUSHNOTIFICATIONS_TASK_CLSID_STRING _STRINGIZE(PUSHNOTIFICATIONS_TASK_CLSID_UUID) +#define PUSHNOTIFICATIONS_LRP_CLSID_UUID 60FC21B2-B396-4D49-94F0-7555869FB93C + +#define PUSHNOTIFICATIONS_FOREGROUNDSINK_CLSID_UUID 25604D55-9B17-426F-9D67-2B11B3A65598 #if defined(WINDOWSAPPSDK_BUILD_PIPELINE) && (WINDOWSAPPSDK_BUILD_PIPELINE == 1) #include "PushNotifications-Override.h" diff --git a/dev/PushNotifications/PushNotifications.idl b/dev/PushNotifications/PushNotifications.idl index 838ebd5331..1ed562dca9 100644 --- a/dev/PushNotifications/PushNotifications.idl +++ b/dev/PushNotifications/PushNotifications.idl @@ -78,8 +78,6 @@ namespace Microsoft.Windows.PushNotifications [experimental] runtimeclass PushNotificationChannel { - PushNotificationChannel(Windows.Networking.PushNotifications.PushNotificationChannel channel); - // The Channel Uri for app to Post a notification to. Windows.Foundation.Uri Uri { get; }; diff --git a/dev/PushNotifications/PushNotifications.vcxitems b/dev/PushNotifications/PushNotifications.vcxitems index 5f30e4cff9..5f0fb92680 100644 --- a/dev/PushNotifications/PushNotifications.vcxitems +++ b/dev/PushNotifications/PushNotifications.vcxitems @@ -33,6 +33,9 @@ + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/NotificationsLongRunningProcess.idl b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/NotificationsLongRunningProcess.idl new file mode 100644 index 0000000000..b4833b8ab4 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/NotificationsLongRunningProcess.idl @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +import "oaidl.idl"; +import "ocidl.idl"; + +#include "../PushNotifications-Constants.h" + +[object] +[uuid(PUSHNOTIFICATIONS_FOREGROUNDSINK_CLSID_UUID)] +[pointer_default(unique)] +interface IWpnForegroundSink : IUnknown +{ + HRESULT InvokeAll([in] ULONG length, [in, size_is(length)] byte* data, [out] BOOL* foregroundHandled); +}; + +[object] +[uuid(PUSHNOTIFICATIONS_LRP_CLSID_UUID)] +[pointer_default(unique)] +interface INotificationsLongRunningPlatform : IUnknown +{ + HRESULT RegisterLongRunningActivator([in] LPCWSTR processName); + + HRESULT UnregisterLongRunningActivator([in] LPCWSTR processName); + + HRESULT RegisterFullTrustApplication([in] LPCWSTR processName, [in] GUID remoteId, [out] LPWSTR* appId); + + HRESULT RegisterForegroundActivator([in] IWpnForegroundSink* sink, [in] LPCWSTR processName); + + HRESULT UnregisterForegroundActivator([in] LPCWSTR processName); +}; + +[uuid(PUSHNOTIFICATIONS_LIBID_UUID)] +[version(1.0)] +library NotificationsLongRunningPlatformLibrary +{ + [uuid(PUSHNOTIFICATIONS_IMPL_CLSID_UUID)] + coclass NotificationsLongRunningPlatform + { + [default] interface INotificationsLongRunningPlatform; + }; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.def b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.def new file mode 100644 index 0000000000..753f222d5b --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.def @@ -0,0 +1,3 @@ +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj new file mode 100644 index 0000000000..f19a675cf6 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj @@ -0,0 +1,475 @@ + + + + + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + 16.0 + Win32Proj + {BF3FCED0-CADB-490A-93A7-4D90E1F45AB0} + PushNotificationsLongRunningTask.ProxyStub + 10.0 + PushNotificationsLongRunningTask.ProxyStub + true + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + true + v142 + Unicode + + + DynamicLibrary + false + true + v142 + Unicode + + + DynamicLibrary + false + true + v142 + Unicode + + + DynamicLibrary + false + true + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + PostBuildEvent + $(PlatformTarget)\$(Configuration)\ + + + true + PostBuildEvent + + + false + PostBuildEvent + + + true + PostBuildEvent + + + false + PostBuildEvent + + + false + PostBuildEvent + $(PlatformTarget)\$(Configuration)\ + + + true + PostBuildEvent + + + false + PostBuildEvent + + + + + MultiThreadedDebug + + + + %(IgnoreSpecificDefaultLibraries);libucrtd.lib + %(AdditionalOptions) /defaultlib:ucrtd.lib + + + + + + MultiThreaded + + + + %(IgnoreSpecificDefaultLibraries);libucrt.lib + %(AdditionalOptions) /defaultlib:ucrt.lib + + + + + Level4 + true + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + _ARM_;WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + true + true + _ARM_;WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + true + true + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + true + true + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + true + true + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + + + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + Level4 + true + true + true + NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + Windows + true + true + DebugFull + onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + PushNotificationsLongRunningTask.ProxyStub.def + + + dlldata.c + false + $(PlatformTarget)\$(Configuration)\ + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj.filters b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj.filters new file mode 100644 index 0000000000..8eb19e2809 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/PushNotificationsLongRunningTask.ProxyStub.vcxproj.filters @@ -0,0 +1,50 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + Source Files + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/framework.h b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/framework.h new file mode 100644 index 0000000000..cfc853b95d --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/framework.h @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/packages.config b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/packages.config new file mode 100644 index 0000000000..390175e9d3 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.h b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.h new file mode 100644 index 0000000000..0d9fb298f7 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.ProxyStub/pch.h @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#ifndef PCH_H +#define PCH_H + +#include "framework.h" + +#endif //PCH_H diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj new file mode 100644 index 0000000000..522bf2a842 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj @@ -0,0 +1,257 @@ + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + 16.0 + Win32Proj + {1debbff6-ee6e-4944-9de2-35b7a686af42} + PushNotificationsLongRunningTask.StartupTask + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + true + + + false + + + + Level4 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + DebugFull + + + + + Level4 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + Guard + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + true + true + DebugFull + + + + + Level4 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + DebugFull + + + + + Level4 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + Guard + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + true + true + DebugFull + + + + + Level4 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + DebugFull + + + + + Level4 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Guard + stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub + + + Windows + true + true + DebugFull + + + + + + + + + + + + + + + {bf3fced0-cadb-490a-93a7-4d90e1f45ab0} + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj.filters b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj.filters new file mode 100644 index 0000000000..76d9cae5d8 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/PushNotificationsLongRunningTask.StartupTask.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + + + + Header Files + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/packages.config b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/packages.config new file mode 100644 index 0000000000..390175e9d3 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.h b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.h new file mode 100644 index 0000000000..a41f481d20 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/pch.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +// Temporarily disable C4324 because WRL generates a false (well, irrelevant) warning +// 'Microsoft::WRL::Details::StaticStorage::GenericReleaseNotifier,Microsoft::WRL::Details::StorageInstance::OutOfProcCallbackBuffer1,ModuleT>': structure was padded due to alignment specifier +/*#pragma warning(push) +#pragma warning(disable:4324) +#include +#pragma warning(pop)*/ + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/winmain.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/winmain.cpp new file mode 100644 index 0000000000..fb160e78ed --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask.StartupTask/winmain.cpp @@ -0,0 +1,51 @@ +#include +#include "pch.h" + +#include "../PushNotifications-Constants.h" + +#include +#include + +inline bool isRetriableRpcError(HRESULT hr) +{ + return (hr == HRESULT_FROM_WIN32(EPT_S_NOT_REGISTERED)) || + (hr == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE)) || + (hr == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)) || + (hr == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE)) || + (hr == HRESULT_FROM_WIN32(RPC_S_UNKNOWN_IF)); +} + +int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, PSTR /*lpCmdLine*/, int /*nCmdShow*/) +{ + RETURN_IF_FAILED(::CoInitializeEx(nullptr, COINITBASE_MULTITHREADED)); + + auto scopeExit = wil::scope_exit( + [&]() { CoUninitialize(); }); + + unsigned int retries = 0; + HRESULT hr = S_OK; + + while (retries < 3) + { + try + { + wil::com_ptr longRunningProcessPlatform + { wil::CoCreateInstance(CLSCTX_LOCAL_SERVER) }; + + break; + } + catch (...) + { + hr = wil::ResultFromCaughtException(); + + if (!isRetriableRpcError(hr)) + { + break; + } + + retries++; + } + } + + return hr; +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.cpp new file mode 100644 index 0000000000..567363bcc7 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.cpp @@ -0,0 +1,33 @@ +#pragma once + +#include "pch.h" + +void ForegroundSinkManager::Add(std::wstring const& appId, IWpnForegroundSink* const& sink) +{ + auto lock = m_lock.lock_exclusive(); + m_foregroundMap[appId] = sink; +} + +void ForegroundSinkManager::Remove(std::wstring const& appId) +{ + auto lock = m_lock.lock_exclusive(); + m_foregroundMap.erase(appId); +} + +bool ForegroundSinkManager::InvokeForegroundHandlers(std::wstring const& appId, winrt::com_array const& payload, ULONG const& payloadSize) +{ + auto lock = m_lock.lock_exclusive(); + + auto it = m_foregroundMap.find(appId); + if (it != m_foregroundMap.end()) + { + BOOL foregroundHandled = true; + if (FAILED(it->second->InvokeAll(payloadSize, payload.data(), &foregroundHandled))) + { + m_foregroundMap.erase(appId); + return false; + } + return foregroundHandled; + } + return false; +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.h b/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.h new file mode 100644 index 0000000000..9439ec6fc1 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/ForegroundSinkManager.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include +#include +#include +#include + +class ForegroundSinkManager +{ +public: + ForegroundSinkManager() = default; + + void Add(std::wstring const& appId, IWpnForegroundSink* const& sink); + + void Remove(std::wstring const& appId); + + bool InvokeForegroundHandlers(std::wstring const& appId, winrt::com_array const& payload, ULONG const& payloadSize); + +private: + // An app can only have one activate foreground sink with Long Running Process. + // Event handlers in the sink are managed by the WindowsAppSDK. + std::unordered_map> m_foregroundMap = {}; + wil::srwlock m_lock; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.cpp new file mode 100644 index 0000000000..f2d2050e47 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.cpp @@ -0,0 +1,30 @@ +#include "pch.h" +#include "../PushNotificationUtility.h" + +HRESULT NotificationListener::RuntimeClassInitialize( + std::shared_ptr foregroundSinkManager, + std::wstring appId, + std::wstring processName) +{ + m_foregroundSinkManager = foregroundSinkManager; + + m_appId = appId; + m_processName = processName; + + return S_OK; +} + +STDMETHODIMP_(HRESULT __stdcall) NotificationListener::OnRawNotificationReceived(unsigned int payloadLength, _In_ byte* payload, _In_ HSTRING /*correlationVector*/) noexcept try +{ + auto lock = m_lock.lock_exclusive(); + + winrt::com_array payloadArray{ payload, payload + (payloadLength * sizeof(uint8_t)) }; + + if (!m_foregroundSinkManager->InvokeForegroundHandlers(m_appId, payloadArray, payloadLength)) + { + THROW_IF_FAILED(winrt::Microsoft::Windows::PushNotifications::Helpers::ProtocolLaunchHelper(m_processName, payloadLength, payload)); + }; + + return S_OK; +} +CATCH_RETURN() diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.h b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.h new file mode 100644 index 0000000000..a02dc7525b --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListener.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class NotificationListener : public Microsoft::WRL::RuntimeClass<::ABI::Microsoft::Internal::PushNotifications::INotificationListener> +{ +public: + HRESULT RuntimeClassInitialize(std::shared_ptr foregroundSinkManager, std::wstring appId, std::wstring processName); + + STDMETHOD(OnRawNotificationReceived)(unsigned int payloadLength, _In_ byte* payload, _In_ HSTRING correlationVector) noexcept; + +private: + std::shared_ptr m_foregroundSinkManager; + + std::wstring m_appId; + std::wstring m_processName; + + wil::srwlock m_lock; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.cpp new file mode 100644 index 0000000000..642d161c30 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.cpp @@ -0,0 +1,51 @@ +#include "pch.h" + +using namespace Microsoft::WRL; +using namespace ::ABI::Microsoft::Internal::PushNotifications; + +void NotificationListenerManager::Initialize(std::shared_ptr foregroundSinkManager) +{ + m_foregroundSinkManager = foregroundSinkManager; +} + +void NotificationListenerManager::SetAppIdMapping(std::map& appIdList) +{ + for (auto appData : appIdList) + { + AddListener(appData.first, appData.second); + } +} + +void NotificationListenerManager::AddListener(std::wstring const& appId, std::wstring const& processName) +{ + THROW_HR_IF(E_INVALIDARG, appId.empty()); + THROW_HR_IF(E_INVALIDARG, processName.empty()); + + auto lock = m_lock.lock_exclusive(); + + // Make sure we keep the long running sink up-to-date with wpncore. + ComPtr newListener; + THROW_IF_FAILED(MakeAndInitialize(&newListener, m_foregroundSinkManager, appId, processName)); + THROW_IF_FAILED(PushNotifications_RegisterNotificationSinkForFullTrustApplication(appId.c_str(), newListener.Get())); + + AgileRef newListenerAsAgile; + THROW_IF_FAILED(AsAgile(newListener.Get(), &newListenerAsAgile)); + m_notificationListeners[appId] = newListenerAsAgile; +} + +void NotificationListenerManager::RemoveListener(std::wstring appId) +{ + THROW_HR_IF(E_INVALIDARG, appId.empty()); + + auto lock = m_lock.lock_exclusive(); + + LOG_IF_FAILED(PushNotifications_UnregisterNotificationSinkForFullTrustApplication(appId.c_str())); + + m_notificationListeners.erase(appId); +} + +bool NotificationListenerManager::IsEmpty() +{ + auto lock = m_lock.lock_shared(); + return m_notificationListeners.empty(); +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.h b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.h new file mode 100644 index 0000000000..46502cc123 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/NotificationListenerManager.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +class NotificationListenerManager +{ +public: + NotificationListenerManager() {}; + + // This function has to be called after initializing the ForegroundSinkManager during Platform initialization + void Initialize(std::shared_ptr foregroundSinkManager); + + void SetAppIdMapping(std::map& appIdList); + + void AddListener(std::wstring const& appId, std::wstring const& processName); + void RemoveListener(std::wstring appId); + + bool IsEmpty(); + +private: + wil::srwlock m_lock; + + std::map m_notificationListeners; + std::shared_ptr m_foregroundSinkManager; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.cpp new file mode 100644 index 0000000000..d732e1f14b --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.cpp @@ -0,0 +1,43 @@ +#include "pch.h" + +#include "PlatformLifetimeManager.h" + +void PlatformLifetimeManager::Setup() +{ + auto lock = m_lock.lock_exclusive(); + + m_timer.reset(CreateThreadpoolTimer( + [](PTP_CALLBACK_INSTANCE, _Inout_ PVOID shutdownTimerManagerPtr, _Inout_ PTP_TIMER) + { + PlatformLifetimeManager* shutdownTimerManager = reinterpret_cast(shutdownTimerManagerPtr); + shutdownTimerManager->SignalEvent(); + }, + this, + nullptr)); + + THROW_LAST_ERROR_IF_NULL(m_timer); + + // Negative times in SetThreadpoolTimer are relative. Allow 5 seconds to fire. + FILETIME dueTime{}; + *reinterpret_cast(&dueTime) = -static_cast(5000 * 10000); + + SetThreadpoolTimer(m_timer.get(), &dueTime, 0, 0); + + m_event.ResetEvent(); +} + +void PlatformLifetimeManager::Cancel() +{ + auto lock = m_lock.lock_exclusive(); + m_timer.reset(); +} + +void PlatformLifetimeManager::Wait() +{ + m_event.wait(); +} + +void PlatformLifetimeManager::SignalEvent() +{ + m_event.SetEvent(); +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.h b/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.h new file mode 100644 index 0000000000..705fb9ce3b --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/PlatformLifetimeManager.h @@ -0,0 +1,23 @@ +#pragma once + +class PlatformLifetimeManager +{ +public: + + PlatformLifetimeManager() {}; + + void Setup(); + + void Cancel(); + + void Wait(); + + void SignalEvent(); + +private: + + wil::srwlock m_lock; + + wil::unique_event m_event{ wil::EventOptions::None }; + wil::unique_threadpool_timer m_timer; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj b/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj index 50c8cf5ef4..6ccf99c489 100644 --- a/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj @@ -3,6 +3,7 @@ + Debug @@ -125,10 +126,12 @@ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) @@ -141,12 +144,14 @@ true stdcpp17 Guard + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows true true DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) @@ -156,10 +161,12 @@ _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp17 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) @@ -172,12 +179,14 @@ true stdcpp17 Guard + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows true true DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) @@ -186,11 +195,13 @@ true _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true - stdcpp17 + stdcpp20 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) @@ -202,30 +213,52 @@ NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Guard - stdcpp17 + stdcpp20 + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)..\WindowsAppSDK_BootstrapDLL Windows true true DebugFull + Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + {bf3fced0-cadb-490a-93a7-4d90e1f45ab0} + + + + + @@ -237,5 +270,9 @@ + + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj.filters b/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj.filters index 2b46b70322..53d4e755e9 100644 --- a/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj.filters +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/PushNotificationsLongRunningTask.vcxproj.filters @@ -18,6 +18,24 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -26,6 +44,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/packages.config b/dev/PushNotifications/PushNotificationsLongRunningTask/packages.config index b4b0aa9742..7bcf3cf398 100644 --- a/dev/PushNotifications/PushNotificationsLongRunningTask/packages.config +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/packages.config @@ -1,6 +1,9 @@  + + + \ No newline at end of file diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/pch.h b/dev/PushNotifications/PushNotificationsLongRunningTask/pch.h index 24a1188504..b32cb9b6d3 100644 --- a/dev/PushNotifications/PushNotificationsLongRunningTask/pch.h +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/pch.h @@ -4,3 +4,36 @@ #pragma once #include +#include + +// Temporarily disable C4324 because WRL generates a false (well, irrelevant) warning +// 'Microsoft::WRL::Details::StaticStorage::GenericReleaseNotifier,Microsoft::WRL::Details::StorageInstance::OutOfProcCallbackBuffer1,ModuleT>': structure was padded due to alignment specifier +#pragma warning(push) +#pragma warning(disable:4324) +#include +#pragma warning(pop) + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +// UDK/ProxyStub files +#define MIDL_NS_PREFIX +#include +#include + +// LRP files +#include "ForegroundSinkManager.h" +#include "PlatformLifetimeManager.h" +#include "NotificationListener.h" +#include "NotificationListenerManager.h" diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/platform.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/platform.cpp new file mode 100644 index 0000000000..c590fadc15 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/platform.cpp @@ -0,0 +1,188 @@ +#include "pch.h" + +#include "platform.h" +#include "platformfactory.h" +#include + +using namespace winrt::Windows; + +void NotificationsLongRunningPlatformImpl::Initialize() +{ + auto lock = m_lock.lock_exclusive(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + if (m_initialized) + { + return; + } + + // Schedule event signaling to happen in 5 seconds. + // This is in case we later realize there are no apps to be tracked in the LRP. + m_lifetimeManager.Setup(); + + m_storage = Storage::ApplicationData::Current().LocalSettings().CreateContainer( + L"LRP", Storage::ApplicationDataCreateDisposition::Always); + + m_foregroundSinkManager = std::make_shared(); + m_notificationListenerManager.Initialize(m_foregroundSinkManager); + + auto fullTrustApps = GetFullTrustApps(); + + if (!fullTrustApps.empty()) + { + // We have at least one app that could receive notifications. + // Cancel the timer to persist the LRP. + m_lifetimeManager.Cancel(); + m_notificationListenerManager.SetAppIdMapping(fullTrustApps); + } + + m_initialized = true; +} + +void NotificationsLongRunningPlatformImpl::Shutdown() noexcept +{ + auto lock = m_lock.lock_exclusive(); + if (m_shutdown) + { + return; + } + + m_shutdown = true; +} + +void NotificationsLongRunningPlatformImpl::WaitForLifetimeEvent() +{ + m_lifetimeManager.Wait(); +} + +STDMETHODIMP_(HRESULT __stdcall) NotificationsLongRunningPlatformImpl::RegisterFullTrustApplication( + _In_ PCWSTR processName, GUID remoteId, _Out_ PWSTR* appId) noexcept try +{ + auto lock = m_lock.lock_exclusive(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + const std::wstring appIdentifier = GetAppIdentifier(processName); + THROW_IF_FAILED(PushNotifications_RegisterFullTrustApplication(appIdentifier.c_str(), remoteId)); + + wil::unique_cotaskmem_string outAppId = wil::make_unique_string(appIdentifier.c_str()); + *appId = outAppId.release(); + + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP_(HRESULT __stdcall) NotificationsLongRunningPlatformImpl::RegisterLongRunningActivator(_In_ PCWSTR processName) noexcept try +{ + auto lock = m_lock.lock_shared(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + const std::wstring appId = GetAppIdentifier(processName); + m_notificationListenerManager.AddListener(appId, processName); + + m_lifetimeManager.Cancel(); + + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP_(HRESULT __stdcall) NotificationsLongRunningPlatformImpl::UnregisterLongRunningActivator(_In_ PCWSTR processName) noexcept try +{ + auto lock = m_lock.lock_shared(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + const std::wstring appId = GetAppIdentifier(processName); + m_notificationListenerManager.RemoveListener(appId); + m_foregroundSinkManager->Remove(appId); + + RemoveAppIdentifier(appId); + + if (m_notificationListenerManager.IsEmpty()) + { + m_lifetimeManager.Setup(); + } + + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP_(HRESULT __stdcall) NotificationsLongRunningPlatformImpl::RegisterForegroundActivator(_In_ IWpnForegroundSink* sink, _In_ PCWSTR processName) noexcept try +{ + auto lock = m_lock.lock_exclusive(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + const std::wstring appId = GetAppIdentifier(processName); + + m_notificationListenerManager.AddListener(appId, processName); + m_foregroundSinkManager->Add(appId, sink); + + m_lifetimeManager.Cancel(); + + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP_(HRESULT __stdcall) NotificationsLongRunningPlatformImpl::UnregisterForegroundActivator(_In_ PCWSTR processName) noexcept try +{ + auto lock = m_lock.lock_exclusive(); + THROW_HR_IF(WPN_E_PLATFORM_UNAVAILABLE, m_shutdown); + + const std::wstring appId = GetAppIdentifier(processName); + m_foregroundSinkManager->Remove(appId); + + return S_OK; +} +CATCH_RETURN() + +// Returns a map of key-value pairs, where key is appId and value is processName. +// It should only be called by Initialize(), which already acquired a lock. +std::map NotificationsLongRunningPlatformImpl::GetFullTrustApps() +{ + std::map mapOfFullTrustApps; + + // Get list of full trust apps with valid channels from wpncore + wil::unique_cotaskmem_array_ptr appIds; + PushNotifications_GetFullTrustApplicationsWithChannels(appIds.addressof(), appIds.size_address()); + + // Get list of apps from Storage + auto values{ m_storage.Values() }; + + for (size_t i = 0; i < appIds.size(); ++i) + { + if (values.HasKey(appIds[i])) + { + winrt::hstring processName{ winrt::unbox_value(values.Lookup(appIds[i])) }; + mapOfFullTrustApps.emplace(reinterpret_cast(appIds[i]), processName.c_str()); + } + } + + return mapOfFullTrustApps; +} + +const std::wstring NotificationsLongRunningPlatformImpl::GetAppIdentifier(std::wstring const& processName) +{ + auto values{ m_storage.Values() }; + + for (auto it = values.begin(); it != values.end(); it++) + { + winrt::hstring settingValue{ winrt::unbox_value(it.Current().Value()) }; + if (processName.compare(settingValue.c_str()) == 0) + { + return std::wstring{ it.Current().Key().c_str() }; + } + } + + GUID guidReference; + THROW_IF_FAILED(CoCreateGuid(&guidReference)); + + wil::unique_cotaskmem_string guidStr; + THROW_IF_FAILED(StringFromCLSID(guidReference, &guidStr)); + + m_storage.Values().Insert(guidStr.get(), winrt::box_value(processName.c_str())); + return guidStr.get(); +} + +void NotificationsLongRunningPlatformImpl::RemoveAppIdentifier(std::wstring const& appId) +{ + auto values{ m_storage.Values() }; + values.Remove(appId); +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/platform.h b/dev/PushNotifications/PushNotificationsLongRunningTask/platform.h new file mode 100644 index 0000000000..cd4a86a53d --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/platform.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../PushNotifications-Constants.h" + +struct __declspec(uuid(PUSHNOTIFICATIONS_IMPL_CLSID_STRING)) NotificationsLongRunningPlatformImpl: + winrt::implements +{ + void Initialize(); + + void Shutdown() noexcept; + + void WaitForLifetimeEvent(); + + /* INotificationsLongRunningPlatform functions */ + + STDMETHOD(RegisterFullTrustApplication)(_In_ PCWSTR processName, GUID remoteId, _Out_ PWSTR* appId) noexcept; + + STDMETHOD(RegisterLongRunningActivator)(_In_ PCWSTR processName) noexcept; + + STDMETHOD(UnregisterLongRunningActivator)(_In_ PCWSTR processName) noexcept; + + STDMETHOD(RegisterForegroundActivator)(_In_ IWpnForegroundSink* sink, _In_ PCWSTR processName) noexcept; + + STDMETHOD(UnregisterForegroundActivator)(_In_ PCWSTR processName) noexcept; + +private: + + std::map GetFullTrustApps(); + const std::wstring GetAppIdentifier(std::wstring const& processName); + void RemoveAppIdentifier(std::wstring const& processName); + + winrt::Windows::Storage::ApplicationDataContainer m_storage{ nullptr }; + + wil::srwlock m_lock; + + bool m_initialized = false; + bool m_shutdown = false; + + PlatformLifetimeManager m_lifetimeManager{}; + NotificationListenerManager m_notificationListenerManager{}; + std::shared_ptr m_foregroundSinkManager; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.cpp new file mode 100644 index 0000000000..133e3dcf99 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.cpp @@ -0,0 +1,29 @@ +#include "pch.h" + +#include "platform.h" +#include +#include +#include "platformfactory.h" + +using namespace Microsoft::WRL; + +HRESULT NotificationsLongRunningProcessFactory::RuntimeClassInitialize() noexcept try +{ + m_platform = winrt::make_self(); + m_platform->Initialize(); + + return S_OK; +} +CATCH_RETURN() + +IFACEMETHODIMP NotificationsLongRunningProcessFactory::CreateInstance( + _In_opt_ IUnknown* /*outer*/, + _In_ REFIID /*riid*/, + _COM_Outptr_ void** obj) +{ + *obj = m_platform.get(); + // Add one ref per platform instance request. Deref occurs automatically. + m_platform->AddRef(); + + return S_OK; +} diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.h b/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.h new file mode 100644 index 0000000000..03512d8174 --- /dev/null +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/platformfactory.h @@ -0,0 +1,14 @@ +#pragma once + +struct NotificationsLongRunningProcessFactory WrlFinal : public Microsoft::WRL::ClassFactory<> +{ + HRESULT RuntimeClassInitialize() noexcept; + + IFACEMETHODIMP CreateInstance( + _In_opt_ IUnknown* outer, + _In_ REFIID riid, + _COM_Outptr_ void** ppvObject) override; + +private: + winrt::com_ptr m_platform; +}; diff --git a/dev/PushNotifications/PushNotificationsLongRunningTask/winmain.cpp b/dev/PushNotifications/PushNotificationsLongRunningTask/winmain.cpp index 1b2da5cdd5..fd6b5f5911 100644 --- a/dev/PushNotifications/PushNotificationsLongRunningTask/winmain.cpp +++ b/dev/PushNotifications/PushNotificationsLongRunningTask/winmain.cpp @@ -1,10 +1,67 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. -#include #include "pch.h" +// include notifications constants file here + +#include + +// Temporarily disable C4324 because WRL generates a false (well, irrelevant) warning +// 'Microsoft::WRL::Details::StaticStorage::GenericReleaseNotifier,Microsoft::WRL::Details::StorageInstance::OutOfProcCallbackBuffer1,ModuleT>': structure was padded due to alignment specifier +#pragma warning(push) +#pragma warning(disable:4324) +#include +#pragma warning(pop) + +#include "platform.h" +#include "platformfactory.h" + +using namespace Microsoft::WRL; + int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, PSTR /*lpCmdLine*/, int /*nCmdShow*/) { + auto coInitialize = wil::CoInitializeEx(); + + ComPtr platformFactory; + RETURN_IF_FAILED(MakeAndInitialize(&platformFactory)); + + ComPtr platform; + // Get an initialized instance of the LRP platform + RETURN_IF_FAILED(platformFactory->CreateInstance(nullptr, __uuidof(INotificationsLongRunningPlatform), &platform)); + + auto& module = Module::Create(); + + ComPtr platformFactoryAsClassFactory; + RETURN_IF_FAILED(platformFactory.As(&platformFactoryAsClassFactory)); + + CLSID clsid = __uuidof(NotificationsLongRunningPlatformImpl); + DWORD cookie = 0; + RETURN_IF_FAILED(module.RegisterCOMObject( + nullptr, + &clsid, + platformFactoryAsClassFactory.GetAddressOf(), + &cookie, + 1)); + + // Wait returns if the platform realizes there are no more apps to be tracked. + // It also returns if the timer initialized at the process start fires (see NotificationsLongRunningPlatformImpl::Initialize). + platform->WaitForLifetimeEvent(); + platform->Shutdown(); + + RETURN_IF_FAILED(module.UnregisterCOMObject(nullptr, &cookie, 1)); + + module.Terminate(); + return 0; } + +STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hinst); + } + + return TRUE; +} diff --git a/dev/PushNotifications/externs.h b/dev/PushNotifications/externs.h index 19776f45fb..2217362df3 100644 --- a/dev/PushNotifications/externs.h +++ b/dev/PushNotifications/externs.h @@ -7,3 +7,16 @@ wil::unique_event& GetWaitHandleForArgs(); inline const winrt::hstring ACTIVATED_EVENT_ARGS_KEY = L"GlobalActivatedEventArgs"; + +struct ChannelDetails +{ + winrt::hstring channelUri; + winrt::hstring channelId; + winrt::hstring appUserModelId; + winrt::Windows::Foundation::DateTime channelExpiryTime; +}; + +inline HRESULT GetCurrentProcessPath(wil::unique_cotaskmem_string& processName) +{ + return wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr, processName); +}; diff --git a/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj b/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj index f08a3a14cc..05fe960798 100644 --- a/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj +++ b/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj @@ -177,7 +177,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)appmodel\identity;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)appmodel\identity;$(SolutionDir)common Use Level4 true @@ -196,7 +196,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -215,7 +215,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -234,7 +234,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -253,7 +253,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -276,7 +276,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -299,7 +299,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -322,7 +322,7 @@ - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(OutDir)..\DynamicDependency.DataStore.ProxyStub;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub;$(ProjectDir)..\WindowsAppRuntime_Insights;$(SolutionDir)common Use Level4 true @@ -372,6 +372,9 @@ {1a41bb90-cc34-4edd-9a27-50714cdbc169} + + {bf3fced0-cadb-490a-93a7-4d90e1f45ab0} + {1307dd1b-bbe8-4cd0-b1a0-0db6d61eeaa0} diff --git a/dev/WindowsAppRuntime_DLL/pch.h b/dev/WindowsAppRuntime_DLL/pch.h index 5129efb101..a363b87e54 100644 --- a/dev/WindowsAppRuntime_DLL/pch.h +++ b/dev/WindowsAppRuntime_DLL/pch.h @@ -48,4 +48,5 @@ #include #include #include -#include <..\WindowsAppRuntime_Insights\WindowsAppRuntimeInsights.h> + +#define MIDL_NS_PREFIX diff --git a/test/AppLifecycle/FunctionalTests.cpp b/test/AppLifecycle/FunctionalTests.cpp index a727b21825..db8dac3d19 100644 --- a/test/AppLifecycle/FunctionalTests.cpp +++ b/test/AppLifecycle/FunctionalTests.cpp @@ -188,6 +188,18 @@ namespace Test::AppLifecycle WaitForEvent(event, m_failed); } + TEST_METHOD(GetActivatedEventArgsForPush_Win32) + { + // Create a named event for communicating with test app. + auto event = CreateTestEvent(c_testPushPhaseEventName); + + // Launch the test app using the push activationprotocol. + Execute(L"AppLifecycleTestApp.exe", L"----WindowsAppRuntimePushServer:-Payload:\"\"", g_deploymentDir); + + // Wait for the protocol activation. + WaitForEvent(event, m_failed); + } + TEST_METHOD(GetActivatedEventArgsForStartup_Win32) { // Create a named event for communicating with test app. diff --git a/test/PushNotificationTests/APITests.cpp b/test/PushNotificationTests/APITests.cpp index dc792528f3..892c483920 100644 --- a/test/PushNotificationTests/APITests.cpp +++ b/test/PushNotificationTests/APITests.cpp @@ -173,6 +173,15 @@ namespace Test::PushNotifications VERIFY_NO_THROW(LocalBackgroundTask.Run(mockBackgroundTaskInstance)); } + TEST_METHOD(BackgroundActivation_Unpackaged) + { + RunTestUnpackaged(L"BackgroundActivationTest", testWaitTime()); // Need to launch one time to enable background activation. + + auto LocalBackgroundTask = winrt::create_instance(c_comServerId, CLSCTX_ALL); + auto mockBackgroundTaskInstance = winrt::make(); + VERIFY_NO_THROW(LocalBackgroundTask.Run(mockBackgroundTaskInstance)); + } + TEST_METHOD(MultipleBackgroundActivation) { RunTest(L"BackgroundActivationTest", testWaitTime()); // Need to launch one time to enable background activation. @@ -188,19 +197,44 @@ namespace Test::PushNotifications } + TEST_METHOD(MultipleBackgroundActivation_Unpackaged) + { + RunTestUnpackaged(L"BackgroundActivationTest", testWaitTime()); // Need to launch one time to enable background activation. + + auto LocalBackgroundTask1 = winrt::create_instance(c_comServerId, CLSCTX_ALL); + auto mockBackgroundTaskInstance1 = winrt::make(); + + auto LocalBackgroundTask2 = winrt::create_instance(c_comServerId, CLSCTX_ALL); + auto mockBackgroundTaskInstance2 = winrt::make(); + + VERIFY_NO_THROW(LocalBackgroundTask1.Run(mockBackgroundTaskInstance1)); + VERIFY_NO_THROW(LocalBackgroundTask2.Run(mockBackgroundTaskInstance2)); + + } + TEST_METHOD(ChannelRequestUsingNullRemoteId) { RunTest(L"ChannelRequestUsingNullRemoteId", testWaitTime()); } + TEST_METHOD(ChannelRequestUsingNullRemoteId_Unpackaged) + { + RunTestUnpackaged(L"ChannelRequestUsingNullRemoteId", testWaitTime()); + } + TEST_METHOD(ChannelRequestUsingRemoteId) { RunTest(L"ChannelRequestUsingRemoteId", channelTestWaitTime()); } - TEST_METHOD(MultipleChannelClose) + TEST_METHOD(ChannelRequestUsingRemoteId_Unpackaged) + { + RunTestUnpackaged(L"ChannelRequestUsingRemoteId", channelTestWaitTime()); + } + + TEST_METHOD(MultipleChannelClose_Unpackaged) { - RunTest(L"MultipleChannelClose", channelTestWaitTime()); + RunTestUnpackaged(L"MultipleChannelClose", channelTestWaitTime()); } TEST_METHOD(MultipleChannelRequestUsingSameRemoteId) @@ -208,42 +242,72 @@ namespace Test::PushNotifications RunTest(L"MultipleChannelRequestUsingSameRemoteId", channelTestWaitTime()); } + TEST_METHOD(MultipleChannelRequestUsingSameRemoteId_Unpackaged) + { + RunTestUnpackaged(L"MultipleChannelRequestUsingSameRemoteId", channelTestWaitTime()); + } + TEST_METHOD(MultipleChannelRequestUsingMultipleRemoteId) { RunTest(L"MultipleChannelRequestUsingMultipleRemoteId", channelTestWaitTime()); } + TEST_METHOD(MultipleChannelRequestUsingMultipleRemoteId_Unpackaged) + { + RunTestUnpackaged(L"MultipleChannelRequestUsingMultipleRemoteId", channelTestWaitTime()); + } + TEST_METHOD(ActivatorTest) { RunTest(L"ActivatorTest", testWaitTime()); } + TEST_METHOD(ActivatorTest_Unpackaged) + { + RunTestUnpackaged(L"ActivatorTest", channelTestWaitTime()); + } + TEST_METHOD(RegisterActivatorNullDetails) { RunTest(L"RegisterActivatorNullDetails", testWaitTime()); } + TEST_METHOD(RegisterActivatorNullDetails_Unpackaged) + { + RunTestUnpackaged(L"RegisterActivatorNullDetails", testWaitTime()); + } + TEST_METHOD(RegisterActivatorNullClsid) { RunTest(L"RegisterActivatorNullClsid", testWaitTime()); } + TEST_METHOD(RegisterActivatorNullClsid_Unpackaged) + { + RunTestUnpackaged(L"RegisterActivatorNullClsid", testWaitTime()); + } + TEST_METHOD(MultipleRegisterActivatorTest) { RunTest(L"MultipleRegisterActivatorTest", testWaitTime()); } + TEST_METHOD(MultipleRegisterActivatorTest_Unpackaged) + { + RunTestUnpackaged(L"MultipleRegisterActivatorTest", testWaitTime()); + } + TEST_METHOD(VerifyComActivatorSupported) { RunTest(L"VerifyComActivatorSupported", testWaitTime()); } - TEST_METHOD(VerifyComActivatorNotSupported) + TEST_METHOD(VerifyComActivatorNotSupported_Unpackaged) { RunTestUnpackaged(L"VerifyComActivatorNotSupported", testWaitTime()); } - TEST_METHOD(VerifyProtocolActivatorSupported) + TEST_METHOD(VerifyProtocolActivatorSupported_Unpackaged) { RunTestUnpackaged(L"VerifyProtocolActivatorSupported", testWaitTime()); } @@ -258,9 +322,19 @@ namespace Test::PushNotifications RunTest(L"VerifyComAndProtocolActivatorNotSupported", testWaitTime()); } + TEST_METHOD(VerifyComAndProtocolActivatorNotSupported_Unpackaged) + { + RunTestUnpackaged(L"VerifyComAndProtocolActivatorNotSupported", testWaitTime()); + } + TEST_METHOD(VerifyNullActivatorNotSupported) { RunTest(L"VerifyNullActivatorNotSupported", testWaitTime()); } + + TEST_METHOD(VerifyProtocolActivation_Unpackaged) + { + RunTestUnpackaged(L"----WindowsAppRuntimePushServer:-Payload:\"\"", testWaitTime()); + } }; } diff --git a/test/PushNotificationTests/LRPTests.cpp b/test/PushNotificationTests/LRPTests.cpp new file mode 100644 index 0000000000..50974be5ec --- /dev/null +++ b/test/PushNotificationTests/LRPTests.cpp @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include "pch.h" +#include +#include "MockBackgroundTaskInstance.h" +#include + +#include + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::System; + +namespace Test::PushNotifications +{ + class LRPTests + { + + public: + BEGIN_TEST_CLASS(LRPTests) + TEST_CLASS_PROPERTY(L"Description", L"Windows App SDK Push Notifications Long Running Process tests") + TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") + TEST_CLASS_PROPERTY(L"RunAs:Class", L"RestrictedUser") + END_TEST_CLASS() + + TEST_CLASS_SETUP(ClassInit) + { + try + { + TP::AddPackage_WindowsAppRuntimeFramework(); // Installs WASfwk + TP::AddPackage_DynamicDependencyDataStore(); // Installs WASmain + TP::AddPackage_DynamicDependencyLifetimeManager(); // Installs WASddlm + TP::AddPackage_PushNotificationsLongRunningTask(); // Installs the Push Notifications Long Running Process (LRP). + } + catch (...) + { + return false; + } + + return true; + } + + TEST_CLASS_CLEANUP(ClassUninit) + { + try + { + // Remove in reverse order to avoid conflicts between inter-dependent packages. + TP::RemovePackage_PushNotificationsLongRunningTask(); + TP::RemovePackage_DynamicDependencyLifetimeManager(); + TP::RemovePackage_DynamicDependencyDataStore(); + TP::RemovePackage_WindowsAppRuntimeFramework(); + } + catch (...) + { + return false; + } + return true; + } + + TEST_METHOD_SETUP(MethodInit) + { + VERIFY_IS_TRUE(TP::IsPackageRegistered_WindowsAppRuntimeFramework()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_DynamicDependencyDataStore()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_DynamicDependencyLifetimeManager()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_PushNotificationsLongRunningTask()); + return true; + } + + TEST_METHOD_CLEANUP(MethodUninit) + { + VERIFY_IS_TRUE(TP::IsPackageRegistered_WindowsAppRuntimeFramework()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_DynamicDependencyDataStore()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_DynamicDependencyLifetimeManager()); + VERIFY_IS_TRUE(TP::IsPackageRegistered_PushNotificationsLongRunningTask()); + + return true; + } + + TEST_METHOD(LaunchLRP_FromCoCreateInstance) + { + VERIFY_SUCCEEDED(::CoInitializeEx(nullptr, COINITBASE_MULTITHREADED)); + + auto scopeExit = wil::scope_exit( + [&]() { VERIFY_NO_THROW(CoUninitialize()); }); + + VERIFY_NO_THROW(winrt::create_instance(_uuidof(NotificationsLongRunningPlatform), CLSCTX_ALL)); + + // Poll the app status every second. It should be alive for 5 seconds. + for (int i = 0; i < 5; i++) + { + VerifyLRP_IsRunning(true); + Sleep(1000); + } + + // Verify the LRP is not running. + VerifyLRP_IsRunning(false); + } + + TEST_METHOD(LaunchLRP_FromStartupTask) + { + STARTUPINFO startupInfo = { 0 }; + wil::unique_process_information processInfo; + + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + // Build the Solution OutDir path where the Startup Task exe is. + auto startupTaskExePath = Test::FileSystem::GetSolutionOutDirPath(); + startupTaskExePath /= Test::Packages::PushNotificationsLongRunningTask::c_PackageDirName; + startupTaskExePath += L".Msix"; + startupTaskExePath /= L"msix"; + startupTaskExePath /= L"PushNotificationsLongRunningTask.StartupTask.exe"; + + BOOL wasProcessCreated = CreateProcess( + startupTaskExePath.c_str(), + nullptr, // command line options + nullptr, // process attributes + nullptr, // thread attributes + FALSE, // inherit handles + NORMAL_PRIORITY_CLASS, // creation flags + nullptr, // lpEnvironment + nullptr, // current directory for the process + &startupInfo, + &processInfo + ); + + if (wasProcessCreated == FALSE) + { + VERIFY_SUCCEEDED(GetLastError()); + } + + // Wait for the process to come up and be captured in the snapshot from verification step. + Sleep(1000); + + // Poll the app status every second. It should be alive for 5 seconds. + for (int i = 0; i < 5; i++) + { + VerifyLRP_IsRunning(true); + Sleep(1000); + } + + // Verify the LRP is not running. + VerifyLRP_IsRunning(false); + } + + void VerifyLRP_IsRunning(bool isRunning) + { + wil::unique_handle processesSnapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + VERIFY_IS_NOT_NULL(processesSnapshot.get()); + + PROCESSENTRY32 processEntry = { 0 }; + processEntry.dwSize = sizeof(processEntry); + + BOOL result = Process32First(processesSnapshot.get(), &processEntry); + while (result != FALSE) + { + if (wcscmp(L"PushNotificationsLongRunningTask.exe", processEntry.szExeFile) == 0) + { + VERIFY_IS_TRUE(isRunning); + DWORD processId = processEntry.th32ProcessID; + + wil::unique_handle longRunningProcessHandle(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId)); + DWORD exitCode = 0; + BOOL exitCodeProcess = GetExitCodeProcess(longRunningProcessHandle.get(), &exitCode); + + VERIFY_SUCCEEDED(exitCodeProcess == FALSE ? GetLastError() : S_OK); + VERIFY_ARE_EQUAL(exitCode, STILL_ACTIVE); + + return; + } + + result = Process32Next(processesSnapshot.get(), &processEntry); + } + + VERIFY_IS_FALSE(isRunning); + } + + }; +} diff --git a/test/PushNotificationTests/PushNotificationTests.vcxproj b/test/PushNotificationTests/PushNotificationTests.vcxproj index f09cbc5361..947f38d539 100644 --- a/test/PushNotificationTests/PushNotificationTests.vcxproj +++ b/test/PushNotificationTests/PushNotificationTests.vcxproj @@ -126,7 +126,7 @@ true true true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub WIN32;NDEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -144,7 +144,7 @@ Use Level3 true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub WIN32;_DEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -160,7 +160,7 @@ Use Level3 true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub _DEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -176,7 +176,7 @@ Use Level3 true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub _DEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -194,7 +194,7 @@ true true true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub NDEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -214,7 +214,7 @@ true true true - %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL + %(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;..\inc;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(OutDir)..\PushNotificationsLongRunningTask.ProxyStub NDEBUG;%(PreprocessorDefinitions);;INLINE_TEST_METHOD_MARKUP true pch.h @@ -246,6 +246,7 @@ Create Create + @@ -261,6 +262,11 @@ + + + {bf3fced0-cadb-490a-93a7-4d90e1f45ab0} + + {f76b776e-86f5-48c5-8fc7-d2795ecc9746} diff --git a/test/PushNotificationTests/PushNotificationTests.vcxproj.filters b/test/PushNotificationTests/PushNotificationTests.vcxproj.filters index e3415f6d30..fd40c7c2bd 100644 --- a/test/PushNotificationTests/PushNotificationTests.vcxproj.filters +++ b/test/PushNotificationTests/PushNotificationTests.vcxproj.filters @@ -41,6 +41,9 @@ Source Files + + Source Files + @@ -48,11 +51,8 @@ - CopyFiles - - \ No newline at end of file diff --git a/test/PushNotificationTests/pch.h b/test/PushNotificationTests/pch.h index 76f69461d1..dea78ec628 100644 --- a/test/PushNotificationTests/pch.h +++ b/test/PushNotificationTests/pch.h @@ -20,6 +20,7 @@ #include #include +#include #include #include diff --git a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/Makefile.mak b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/Makefile.mak index 28e229b287..e38f77f8b7 100644 --- a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/Makefile.mak +++ b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/Makefile.mak @@ -18,7 +18,7 @@ SIGNTOOL_OPTS=/v !MESSAGE ProjectDir =$(ProjectDir) !MESSAGE OutDir =$(OutDir) !MESSAGE TargetName =$(TargetName) -!MESSAGE WindowsAppSDKBuildPipeline =$(WindowsAppSDKBuildPipeline) +!MESSAGE WindowsAppSDKBuildPipeline =$(WindowsAppSDKBuildPipeline) !ENDIF TARGET_BASENAME=PushNotificationsLongRunningTask @@ -27,6 +27,17 @@ TARGET_EXE=$(TARGET_BASENAME) TARGET_EXE_DIR=$(OutDir)$(TARGET_EXE) TARGET_EXE_FILE=$(TARGET_EXE_DIR)\$(TARGET_EXE).exe +TARGET_STARTUPEXE=$(TARGET_BASENAME).StartupTask +TARGET_STARTUPEXE_DIR=$(OutDir)$(TARGET_STARTUPEXE) +TARGET_STARTUPEXE_FILE=$(TARGET_STARTUPEXE_DIR)\$(TARGET_STARTUPEXE).exe + +TARGET_PROXYSTUB=PushNotificationsLongRunningTask.ProxyStub +TARGET_PROXYSTUB_DIR=$(OutDir)$(TARGET_PROXYSTUB) +TARGET_PROXYSTUB_FILE=$(TARGET_PROXYSTUB_DIR)\$(TARGET_PROXYSTUB).dll +!IFDEF VERBOSE +!MESSAGE $(TARGET_PROXYSTUB_FILE) +!ENDIF + TargetDir=$(OutDir)$(TargetName) WorkDir=$(TargetDir)\msix OutMsix=$(TargetDir)\$(TargetName) @@ -41,9 +52,12 @@ all: build $(OutMsix): $(ProjectDir)appxmanifest.xml @if not exist $(WorkDir) md $(WorkDir) @copy /Y $(ProjectDir)appxmanifest.xml $(WorkDir)\appxmanifest.xml + @copy /Y $(OutDir)WindowsAppRuntime_DLL\Microsoft.Internal.FrameworkUdk.dll $(WorkDir)\Microsoft.Internal.FrameworkUdk.dll @if not exist $(WorkDir)\Assets md $(WorkDir)\Assets >NUL @copy /Y $(ProjectDir)Assets\* $(WorkDir)\Assets\* >NUL @copy /Y $(TARGET_EXE_FILE) $(WorkDir) >NUL + @copy /Y $(TARGET_STARTUPEXE_FILE) $(WorkDir) >NUL + @copy /Y $(TARGET_PROXYSTUB_FILE) $(WorkDir) >NUL @makeappx.exe pack $(MAKEAPPX_OPTS) /o /h SHA256 /d $(WorkDir) /p $(OutMsix) @signtool.exe sign /a $(SIGNTOOL_OPTS) /fd SHA256 /f $(SolutionDir)temp\MSTest.pfx $(OutMsix) diff --git a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj index bb65945377..8b5a2708b5 100644 --- a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj +++ b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj @@ -129,11 +129,20 @@ + + {bf3fced0-cadb-490a-93a7-4d90e1f45ab0} + + + {1debbff6-ee6e-4944-9de2-35b7a686af42} + {1307dd1b-bbe8-4cd0-b1a0-0db6d61eeaa0} + + + - + \ No newline at end of file diff --git a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj.filters b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj.filters index c89a41cd82..d86bd6d01f 100644 --- a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj.filters +++ b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/PushNotificationsLongRunningTask.Msix.vcxproj.filters @@ -17,4 +17,7 @@ - + + + + \ No newline at end of file diff --git a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/appxmanifest.xml b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/appxmanifest.xml index 9f0dac175c..789ada48b2 100644 --- a/test/PushNotifications/PushNotificationsLongRunningTask.Msix/appxmanifest.xml +++ b/test/PushNotifications/PushNotificationsLongRunningTask.Msix/appxmanifest.xml @@ -46,13 +46,25 @@ - + + + + + + + + + + + + + + @@ -250,10 +251,16 @@ $(OutDir)\..\WindowsAppRuntime_DLL\Microsoft.WindowsAppRuntime.dll + + + {f76b776e-86f5-48c5-8fc7-d2795ecc9746} + + + @@ -262,8 +269,9 @@ + - + \ No newline at end of file diff --git a/test/TestApps/PushNotificationsDemoApp/main.cpp b/test/TestApps/PushNotificationsDemoApp/main.cpp index 721e92bd6a..8d82381432 100644 --- a/test/TestApps/PushNotificationsDemoApp/main.cpp +++ b/test/TestApps/PushNotificationsDemoApp/main.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include "WindowsAppRuntime.Test.AppModel.h" using namespace winrt::Microsoft::Windows::AppLifecycle; using namespace winrt::Microsoft::Windows::PushNotifications; @@ -18,7 +20,7 @@ winrt::Windows::Foundation::IAsyncOperation RequestChan // To obtain an AAD RemoteIdentifier for your app, // follow the instructions on https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app auto channelOperation = PushNotificationManager::CreateChannelAsync( - winrt::guid("ccd2ae3f-764f-4ae3-be45-9804761b28b2")); + winrt::guid("0160ee84-0c53-4851-9ff2-d7f5a87ed914")); // Setup the inprogress event handler channelOperation.Progress( @@ -49,7 +51,7 @@ winrt::Windows::Foundation::IAsyncOperation RequestChan auto channelExpiry = result.Channel().ExpirationTime(); // Register Push Event for Foreground - result.Channel().PushReceived([](const auto&, PushNotificationReceivedEventArgs const& args) + winrt::event_token token = result.Channel().PushReceived([](const auto&, PushNotificationReceivedEventArgs const& args) { auto payload = args.Payload(); @@ -89,11 +91,27 @@ winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel RequestCha int main() { - PushNotificationActivationInfo info( - PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, - winrt::guid("ccd2ae3f-764f-4ae3-be45-9804761b28b2")); // same clsid as app manifest + if (!Test::AppModel::IsPackagedProcess()) + { + // Major.Minor version, MinVersion=0 to find any framework package for this major.minor version + const UINT32 c_Version_MajorMinor{ 0x00040001 }; + const PACKAGE_VERSION minVersion{}; + RETURN_IF_FAILED(MddBootstrapInitialize(c_Version_MajorMinor, nullptr, minVersion)); + } - PushNotificationManager::RegisterActivator(info); + if (PushNotificationManager::IsActivatorSupported(PushNotificationRegistrationActivators::ComActivator)) + { + PushNotificationActivationInfo info( + PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, + winrt::guid("ccd2ae3f-764f-4ae3-be45-9804761b28b2")); // same clsid as app manifest + + PushNotificationManager::RegisterActivator(info); + } + else + { + PushNotificationActivationInfo info(PushNotificationRegistrationActivators::ProtocolActivator); + PushNotificationManager::RegisterActivator(info); + } auto args = AppInstance::GetCurrent().GetActivatedEventArgs(); auto kind = args.Kind(); @@ -129,7 +147,15 @@ int main() std::cin.ignore(); } - // Don't unregister PushTrigger because we still want to receive push notifications from background infrastructure. - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::ComActivator); + if (PushNotificationManager::IsActivatorSupported(PushNotificationRegistrationActivators::ComActivator)) + { + // Don't unregister PushTrigger because we still want to receive push notifications from background infrastructure. + PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::ComActivator); + } + + if (!Test::AppModel::IsPackagedProcess()) + { + MddBootstrapShutdown(); + } return 0; } diff --git a/test/TestApps/PushNotificationsDemoApp/packages.config b/test/TestApps/PushNotificationsDemoApp/packages.config index fc20b89bf7..6a9200b4c2 100644 --- a/test/TestApps/PushNotificationsDemoApp/packages.config +++ b/test/TestApps/PushNotificationsDemoApp/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/test/TestApps/PushNotificationsDemoPackage/Package.appxmanifest b/test/TestApps/PushNotificationsDemoPackage/Package.appxmanifest index 71a2571567..65d30def92 100644 --- a/test/TestApps/PushNotificationsDemoPackage/Package.appxmanifest +++ b/test/TestApps/PushNotificationsDemoPackage/Package.appxmanifest @@ -49,7 +49,7 @@ - + diff --git a/test/TestApps/PushNotificationsDemoPackage/PushNotificationsDemoPackage.wapproj b/test/TestApps/PushNotificationsDemoPackage/PushNotificationsDemoPackage.wapproj index edaf9dee12..34f6760256 100644 --- a/test/TestApps/PushNotificationsDemoPackage/PushNotificationsDemoPackage.wapproj +++ b/test/TestApps/PushNotificationsDemoPackage/PushNotificationsDemoPackage.wapproj @@ -42,7 +42,7 @@ ..\PushNotificationsDemoApp\PushNotificationsDemoApp.vcxproj true False - $(SolutionDir)temp\MSTest.pfx + $(SolutionDir)temp\MSTest.pfx SHA256 True True @@ -51,7 +51,7 @@ Always - + Always @@ -67,7 +67,6 @@ Always - Designer diff --git a/test/TestApps/PushNotificationsTestApp/PushNotificationsTestApp.vcxproj b/test/TestApps/PushNotificationsTestApp/PushNotificationsTestApp.vcxproj index bf5aabc79b..4b2ebc71ae 100644 --- a/test/TestApps/PushNotificationsTestApp/PushNotificationsTestApp.vcxproj +++ b/test/TestApps/PushNotificationsTestApp/PushNotificationsTestApp.vcxproj @@ -280,4 +280,4 @@ - + \ No newline at end of file diff --git a/test/TestApps/PushNotificationsTestApp/main.cpp b/test/TestApps/PushNotificationsTestApp/main.cpp index bbfd1bdc24..a8fabd0728 100644 --- a/test/TestApps/PushNotificationsTestApp/main.cpp +++ b/test/TestApps/PushNotificationsTestApp/main.cpp @@ -15,8 +15,6 @@ using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Storage; using namespace winrt::Windows::Storage::Streams; -bool g_registeredActivator = false; - winrt::guid remoteId1(L"a2e4a323-b518-4799-9e80-0b37aeb0d225"); // Generated from ms.portal.azure.com winrt::guid remoteId2(L"CA1A4AB2-AC1D-4EFC-A132-E5A191CA285A"); // Dummy guid from visual studio guid tool generator @@ -114,17 +112,26 @@ bool MultipleChannelRequestUsingMultipleRemoteId() bool ActivatorTest() { - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); - g_registeredActivator = false; - + PushNotificationManager::UnregisterAllActivators(); try { - PushNotificationActivationInfo info( - PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, - c_fakeComServerId); + if(PushNotificationManager::IsActivatorSupported(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator)) + { + PushNotificationActivationInfo info( + PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, + c_fakeComServerId); + + PushNotificationManager::RegisterActivator(info); + PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); + } + else + { + PushNotificationActivationInfo info(PushNotificationRegistrationActivators::ProtocolActivator); + + PushNotificationManager::RegisterActivator(info); + PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::ProtocolActivator); + } - PushNotificationManager::RegisterActivator(info); - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); } catch (...) { @@ -136,9 +143,7 @@ bool ActivatorTest() // Verify calling register activator with null PushNotificationActivationInfo is not allowed. bool RegisterActivatorNullDetails() { - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); - g_registeredActivator = false; - + PushNotificationManager::UnregisterAllActivators(); try { PushNotificationManager::RegisterActivator(nullptr); @@ -153,22 +158,39 @@ bool RegisterActivatorNullDetails() // Verify calling register activator with null clsid is not allowed. bool RegisterActivatorNullClsid() { - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); - g_registeredActivator = false; - - try + PushNotificationManager::UnregisterAllActivators(); + if(PushNotificationManager::IsActivatorSupported(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator)) { - PushNotificationActivationInfo info( - PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, - winrt::guid()); // Null guid + try + { + PushNotificationActivationInfo info( + PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, + winrt::guid()); - PushNotificationManager::RegisterActivator(info); + PushNotificationManager::RegisterActivator(info); + } + catch(...) + { + return to_hresult() == E_INVALIDARG; + } + return false; } - catch (...) + else { - return to_hresult() == E_INVALIDARG; + try + { + PushNotificationActivationInfo info( + PushNotificationRegistrationActivators::ProtocolActivator, + winrt::guid()); + + PushNotificationManager::RegisterActivator(info); + } + catch(...) + { + return false; + } + return true; } - return false; } // Verify registering multiple activators is not allowed. @@ -176,11 +198,19 @@ bool MultipleRegisterActivatorTest() { try { - PushNotificationActivationInfo info( - PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, - c_fakeComServerId); // Fake clsid to test multiple activators + if(PushNotificationManager::IsActivatorSupported(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator)) + { + PushNotificationActivationInfo info( + PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator, + c_fakeComServerId); // Fake clsid to test multiple activators - PushNotificationManager::RegisterActivator(info); + PushNotificationManager::RegisterActivator(info); + } + else + { + PushNotificationActivationInfo info(PushNotificationRegistrationActivators::ProtocolActivator); + PushNotificationManager::RegisterActivator(info); + } } catch (...) { @@ -291,10 +321,7 @@ int main() try { bool testResult = false; auto scope_exit = wil::scope_exit([&] { - if (g_registeredActivator) - { - PushNotificationManager::UnregisterActivator(PushNotificationRegistrationActivators::PushTrigger | PushNotificationRegistrationActivators::ComActivator); - } + PushNotificationManager::UnregisterAllActivators(); ::Test::Bootstrap::CleanupBootstrap(); }); @@ -308,15 +335,13 @@ int main() try winrt::guid(c_comServerId)); // same clsid as app manifest PushNotificationManager::RegisterActivator(info); - g_registeredActivator = true; } - /* TODO: Register ProtocolActivator for unpackaged applications else { - PushNotificationActivationInfo info(PushNotificationRegistrationActivators::ProtocolActivator, nullptr); + PushNotificationActivationInfo info(PushNotificationRegistrationActivators::ProtocolActivator); PushNotificationManager::RegisterActivator(info); } - */ + auto args = AppInstance::GetCurrent().GetActivatedEventArgs(); auto kind = args.Kind(); diff --git a/test/TestApps/PushNotificationsTestApp/packages.config b/test/TestApps/PushNotificationsTestApp/packages.config index fc20b89bf7..6a9200b4c2 100644 --- a/test/TestApps/PushNotificationsTestApp/packages.config +++ b/test/TestApps/PushNotificationsTestApp/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/test/TestApps/PushNotificationsTestAppPackage/Package.appxmanifest b/test/TestApps/PushNotificationsTestAppPackage/Package.appxmanifest index 60ff7a1919..834df05340 100644 --- a/test/TestApps/PushNotificationsTestAppPackage/Package.appxmanifest +++ b/test/TestApps/PushNotificationsTestAppPackage/Package.appxmanifest @@ -49,7 +49,7 @@ - + diff --git a/test/inc/TestDef.h b/test/inc/TestDef.h index cd5b200582..93a41cbb2a 100644 --- a/test/inc/TestDef.h +++ b/test/inc/TestDef.h @@ -17,6 +17,7 @@ static const std::wstring c_testFilePhaseEventName = L"WindowsAppRuntimeTestFile static const std::wstring c_testStartupPhaseEventName = L"WindowsAppRuntimeTestStartupPhaseEventName"; static const std::wstring c_testInstanceRedirectedPhaseEventName = L"WindowsAppRuntimeTestInstanceRedirectedPhaseEventName"; +static const std::wstring c_testPushPhaseEventName = L"WindowsAppRuntimeTestPushPhaseEventName"; inline const winrt::hstring c_rawNotificationPayload = L""; inline IID c_comServerId = winrt::guid("ccd2ae3f-764f-4ae3-be45-9804761b28b2"); // Value from PushNotificationsTestAppPackage ComActivator in appxmanifest. inline IID c_fakeComServerId = winrt::guid("00000000-0000-0000-0000-000000000001"); diff --git a/tools/GeneratePushNotificationsOverrides.ps1 b/tools/GeneratePushNotificationsOverrides.ps1 index 896851e481..cdce32bc72 100644 --- a/tools/GeneratePushNotificationsOverrides.ps1 +++ b/tools/GeneratePushNotificationsOverrides.ps1 @@ -38,26 +38,30 @@ else Write-Output "$Path exists" } -$pushnotifications_clsid_uuid = New-Guid - # Generate the json file $content_json=@" { - "PushNotifications": { - "LIBID": "CE96C745-3017-460E-895B-4FD98E1194F2", - "Impl": { - "CLSID": { - "UUID": "E739C755-0D09-48DF-A468-A5DF0B5422DC" - } - }, - "Task": { - "CLSID": { - "UUID": "$pushnotifications_clsid_uuid" - } - } - } + "LIBID": "CE96C745-3017-460E-895B-4FD98E1194F2", + "ComServer": { + "CLSID": { + "UUID": "E739C755-0D09-48DF-A468-A5DF0B5422DC" + } + }, + "ComInterfaces": { + "LRP": { + "CLSID": { + "UUID": "60FC21B2-B396-4D49-94F0-7555869FB93C" + } + }, + "ForegroundSink": { + "CLSID": { + "UUID": "25604D55-9B17-426F-9D67-2B11B3A65598" + } + } + } } "@ + $file_json = Join-Path $Path 'PushNotifications-Override.json' Write-Output "Writing $file_json..." "$content_json" | Out-File $file_json -Encoding utf8 @@ -88,14 +92,14 @@ $content_h=@" #define PUSHNOTIFICATIONS_IMPL_CLSID_STRING _STRINGIZE(PUSHNOTIFICATIONS_IMPL_CLSID_UUID) #endif -#ifdef PUSHNOTIFICATIONS_TASK_CLSID_UUID -#undef PUSHNOTIFICATIONS_TASK_CLSID_UUID -#define PUSHNOTIFICATIONS_TASK_CLSID_UUID $pushnotifications_clsid_uuid +#ifdef PUSHNOTIFICATIONS_LRP_CLSID_UUID +#undef PUSHNOTIFICATIONS_LRP_CLSID_UUID +#define PUSHNOTIFICATIONS_LRP_CLSID_UUID 60FC21B2-B396-4D49-94F0-7555869FB93C #endif -#ifdef PUSHNOTIFICATIONS_TASK_CLSID_STRING -#undef PUSHNOTIFICATIONS_TASK_CLSID_STRING -#define PUSHNOTIFICATIONS_TASK_CLSID_STRING _STRINGIZE(PUSHNOTIFICATIONS_TASK_CLSID_UUID) +#ifdef PUSHNOTIFICATIONS_FOREGROUNDSINK_CLSID_UUID +#undef PUSHNOTIFICATIONS_FOREGROUNDSINK_CLSID_UUID +#define PUSHNOTIFICATIONS_FOREGROUNDSINK_CLSID_UUID 25604D55-9B17-426F-9D67-2B11B3A65598 #endif "@ $file_h = Join-Path $Path 'PushNotifications-Override.h'