Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 67 additions & 7 deletions dev/PushNotifications/PushNotificationChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "pch.h"
#include <winrt/Windows.Foundation.Metadata.h>
#include "PushNotificationChannel.h"
#include "Microsoft.Windows.PushNotifications.PushNotificationChannel.g.cpp"
#include <winrt\Windows.Networking.PushNotifications.h>
#include <winrt\Windows.Foundation.h>
#include "PushNotificationReceivedEventArgs.h"
#include "externs.h"

namespace winrt::Windows
{
using namespace winrt::Windows::Networking::PushNotifications;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Metadata;
}
namespace winrt::Microsoft
{
Expand All @@ -26,10 +29,12 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation
{
return winrt::Windows::Uri{ m_channel.Uri() };
}

winrt::Windows::DateTime PushNotificationChannel::ExpirationTime()
{
return m_channel.ExpirationTime();
}

void PushNotificationChannel::Close()
{
try
Expand All @@ -48,18 +53,73 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation

winrt::event_token PushNotificationChannel::PushReceived(winrt::Windows::TypedEventHandler<winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel, winrt::Microsoft::Windows::PushNotifications::PushNotificationReceivedEventArgs> handler)
{
return m_channel.PushNotificationReceived([weak_self = get_weak(), handler](auto&&, auto&& args)
if (IsPackagedAppScenario())
{
auto strong = weak_self.get();
if (strong)
return m_channel.PushNotificationReceived([weak_self = get_weak(), handler](auto&&, auto&& args)
{
handler(*strong, winrt::make<winrt::Microsoft::Windows::PushNotifications::implementation::PushNotificationReceivedEventArgs>(args));
};
});
if (auto strong = weak_self.get())
{
handler(*strong, winrt::make<winrt::Microsoft::Windows::PushNotifications::implementation::PushNotificationReceivedEventArgs>(args));
};
});
}
else
{
auto lock = m_lock.lock_exclusive();
if (!m_foregroundHandlerCount++)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: From a clarity perspective, prefer that we don't rely on int to bool conversion. Better to say != 0 or > 0 and convert it into the bool equivalent.

{
wil::com_ptr<INotificationsLongRunningPlatform> notificationsLongRunningPlatform{
wil::CoCreateInstance<NotificationsLongRunningPlatform, INotificationsLongRunningPlatform>(CLSCTX_LOCAL_SERVER) };

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())
{
m_channel.PushNotificationReceived(token);
}
else
Comment thread
pmpurifoy marked this conversation as resolved.
{
auto lock = m_lock.lock_exclusive();
m_foregroundHandlers.remove(token);
if (!--m_foregroundHandlerCount)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Don't rely on int to bool conversion as a practise. Always better to say != 0 to be precise.

{
wil::com_ptr<INotificationsLongRunningPlatform> notificationsLongRunningPlatform{
wil::CoCreateInstance<NotificationsLongRunningPlatform, INotificationsLongRunningPlatform>(CLSCTX_LOCAL_SERVER) };

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<winrt::Microsoft::Windows::PushNotifications::implementation::PushNotificationReceivedEventArgs>(payload, length);
m_foregroundHandlers(*this, args);
*foregroundHandled = args.Handled();
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();
}
}
18 changes: 17 additions & 1 deletion dev/PushNotifications/PushNotificationChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,40 @@

#pragma once
#include "Microsoft.Windows.PushNotifications.PushNotificationChannel.g.h"
#include <NotificationsLongRunningProcess_h.h>

namespace winrt::Microsoft::Windows::PushNotifications::implementation
{
struct PushNotificationChannel : PushNotificationChannelT<PushNotificationChannel>
typedef winrt::Windows::Foundation::TypedEventHandler<
winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel,
winrt::Microsoft::Windows::PushNotifications::PushNotificationReceivedEventArgs> PushNotificationEventHandler;

struct PushNotificationChannel : PushNotificationChannelT<PushNotificationChannel, IWpnForegroundSink>
{
PushNotificationChannel(winrt::Windows::Networking::PushNotifications::PushNotificationChannel const& channel);

winrt::Windows::Foundation::Uri Uri();
winrt::Windows::Foundation::DateTime ExpirationTime();
void Close();

winrt::event_token PushReceived(winrt::Windows::Foundation::TypedEventHandler<Microsoft::Windows::PushNotifications::PushNotificationChannel, Microsoft::Windows::PushNotifications::PushNotificationReceivedEventArgs> handler);
void PushReceived(winrt::event_token const& token) noexcept;

// IWpnForegroundSink
HRESULT __stdcall InvokeAll(_In_ ULONG length, _In_ byte* payload, _Out_ BOOL* foregroundHandled) noexcept;

private:
bool IsPackagedAppScenario();
bool IsBackgroundTaskBuilderAvailable();

const winrt::Windows::Networking::PushNotifications::PushNotificationChannel m_channel{ nullptr };

winrt::event<PushNotificationEventHandler> m_foregroundHandlers;
ULONG m_foregroundHandlerCount = 0;
wil::srwlock m_lock;
};
}

namespace winrt::Microsoft::Windows::PushNotifications::factory_implementation
{
struct PushNotificationChannel : PushNotificationChannelT<PushNotificationChannel, implementation::PushNotificationChannel>
Expand Down
28 changes: 28 additions & 0 deletions dev/PushNotifications/PushNotificationDummyDeferral.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 <winrt/base.h>
#include <winrt/Windows.ApplicationModel.background.h>

struct PushNotificationDummyDeferral : winrt::implements <PushNotificationDummyDeferral, winrt::Windows::ApplicationModel::Background::IBackgroundTaskDeferral>
{
PushNotificationDummyDeferral() {}

void Complete() { };
};

struct PushNotificationDummyDeferralFactory : winrt::implements<PushNotificationDummyDeferralFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance(_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final
{
RETURN_HR_IF(CLASS_E_NOAGGREGATION, aggregateInterface != nullptr);
return winrt::make<PushNotificationDummyDeferral>().as(interfaceId, object);
}

HRESULT __stdcall LockServer(BOOL) noexcept final
{
return S_OK;
}
};
88 changes: 70 additions & 18 deletions dev/PushNotifications/PushNotificationReceivedEventArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Microsoft.Windows.PushNotifications.PushNotificationReceivedEventArgs.g.cpp"
#include <iostream>
#include <externs.h>
#include <PushNotificationDummyDeferral.h>
#include "ValueMarshaling.h"

namespace winrt
Expand All @@ -23,49 +24,100 @@ namespace winrt

namespace winrt::Microsoft::Windows::PushNotifications::implementation
{
PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::IBackgroundTaskInstance const& backgroundTask): m_backgroundTaskInstance(backgroundTask), m_rawNotification(backgroundTask.TriggerDetails().as<RawNotification>().ContentBytes()) {}
PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(winrt::IBackgroundTaskInstance const& backgroundTask):
m_backgroundTaskInstance(backgroundTask),
m_rawNotificationPayload(BuildPayload(backgroundTask.TriggerDetails().as<RawNotification>().ContentBytes())),
m_unpackagedAppScenario(false) {}

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) {}

PushNotificationReceivedEventArgs::PushNotificationReceivedEventArgs(byte* const& payload, ULONG const& length) :
m_rawNotificationPayload(BuildPayload(payload, length)),
m_unpackagedAppScenario(true) {}

std::vector<uint8_t> PushNotificationReceivedEventArgs::BuildPayload(winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
return { buffer.data(), buffer.data() + (buffer.Length() * sizeof(uint8_t)) };
}

std::vector<uint8_t> PushNotificationReceivedEventArgs::BuildPayload(byte* const& payload, ULONG const& length)
{
return { payload, payload + (length * sizeof(uint8_t)) };
}

winrt::com_array<uint8_t> 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<PushNotificationDummyDeferral>();
return dummyDeferral.as<winrt::BackgroundTaskDeferral>();
}
}

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

10 changes: 9 additions & 1 deletion dev/PushNotifications/PushNotificationReceivedEventArgs.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "Microsoft.Windows.PushNotifications.PushNotificationReceivedEventArgs.g.h"
#include <NotificationsLongRunningProcess_h.h>

namespace winrt::Microsoft::Windows::PushNotifications::implementation
{
Expand All @@ -9,6 +10,7 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation

PushNotificationReceivedEventArgs(winrt::Windows::ApplicationModel::Background::IBackgroundTaskInstance const& backgroundTask);
PushNotificationReceivedEventArgs(winrt::Windows::Networking::PushNotifications::PushNotificationReceivedEventArgs const& args);
PushNotificationReceivedEventArgs(byte* const& payload, ULONG const& length);

com_array<uint8_t> Payload();
winrt::Windows::ApplicationModel::Background::BackgroundTaskDeferral GetDeferral();
Expand All @@ -18,8 +20,14 @@ namespace winrt::Microsoft::Windows::PushNotifications::implementation
void Handled(bool value);

private:
const winrt::Windows::Storage::Streams::IBuffer m_rawNotification{};
std::vector<uint8_t> BuildPayload(winrt::Windows::Storage::Streams::IBuffer const& buffer);
std::vector<uint8_t> BuildPayload(byte* const& payload, ULONG const& length);

std::vector<uint8_t> 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;
};
}
5 changes: 3 additions & 2 deletions dev/PushNotifications/PushNotifications.vcxitems
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
Expand All @@ -10,7 +10,7 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ItemGroup>
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
Expand All @@ -35,5 +35,6 @@
<ClInclude Include="$(MSBuildThisFileDirectory)PushNotificationManager.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)PushNotificationReceivedEventArgs.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)PushNotificationRegistrationToken.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)PushNotificationDummyDeferral.h" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@ import "ocidl.idl";

#include "../PushNotifications-Constants.h"

[object]
[uuid(25604D55-9B17-426F-9D67-2B11B3A65598)]
[pointer_default(unique)]
interface IWpnForegroundSink : IUnknown
{
HRESULT InvokeAll([in] ULONG length, [in, size_is(length)] byte* data, [out] BOOL* foregroundHandled);
};

[object]
[uuid(60FC21B2-B396-4D49-94F0-7555869FB93C)]
[pointer_default(unique)]
interface INotificationsLongRunningPlatform : IUnknown
{
HRESULT RegisterFullTrustApplication([in] LPCWSTR processName, [in] GUID remoteId, [out] GUID* appId);

HRESULT RegisterForegroundActivator([in] IWpnForegroundSink* sink, [in] LPCWSTR processName);

HRESULT UnregisterForegroundActivator([in] LPCWSTR processName);

HRESULT SendBackgroundNotification([in] LPCWSTR processName, [in] ULONG length, [in, size_is(length)] byte* data);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove from idl. No longer being used anywhere.


};

[uuid(PUSHNOTIFICATIONS_LIBID_UUID)]
Expand Down
Loading