Skip to content

Introduce Register API to take in assets#2447

Closed
sharath2727 wants to merge 4 commits intomainfrom
user/vemancha/registeroverload
Closed

Introduce Register API to take in assets#2447
sharath2727 wants to merge 4 commits intomainfrom
user/vemancha/registeroverload

Conversation

@sharath2727
Copy link
Copy Markdown
Contributor

@sharath2727 sharath2727 commented Apr 28, 2022

Description: Allow developers to input DisplayName and Icon of their choice to be registered with AppNotificationManager so that these assets can show up when AppNotifications are posted or received.

Validation: Introduced unittests and validated them.

@ghost ghost added the needs-triage label Apr 28, 2022
@sharath2727 sharath2727 force-pushed the user/vemancha/registeroverload branch from f429687 to 7a0a03a Compare April 28, 2022 06:55
@loneursid loneursid added the area-Notifications Toast notification, badges, Live Tiles, push notifications label Apr 28, 2022
@ghost
Copy link
Copy Markdown

ghost commented Apr 28, 2022

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.

❌ sharath2727 sign now
You have signed the CLA already but the status is still pending? Let us recheck it.

@sharath2727 sharath2727 marked this pull request as ready for review April 28, 2022 21:36
@sharath2727 sharath2727 changed the title Introduce AppNotificationManager::Register API to take in assets Introduce Register API to take in assets Apr 28, 2022
@sharath2727 sharath2727 requested a review from pmpurifoy April 28, 2022 22:35
Comment thread dev/Common/Common.vcxitems.filters
Comment thread dev/AppNotifications/AppNotificationManager.cpp Outdated
Comment thread dev/AppNotifications/AppNotificationManager.cpp Outdated
Comment thread dev/AppNotifications/AppNotificationManager.cpp Outdated
Comment thread dev/AppNotifications/AppNotificationManager.cpp Outdated
Comment thread dev/AppNotifications/AppNotificationManager.cpp Outdated
Comment thread dev/AppNotifications/AppNotificationUtility.cpp Outdated
int main()
{
auto manager = winrt::AppNotificationManager::Default();
manager.Register(L"AppNotifications", winrt::Windows::Foundation::Uri {L"<Path to Icon>"});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's okay to set a mock path (C:\Users\<user>\AppData\MyApp\icon.png or the app folder which may be under ProgramFiles?).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Would like to see what everyone thinks of this before updating. Thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like more specific over too general, as long as there aren't too many options and that we list the most common options. Maybe we use :\Users<user>\AppData\MyApp\icon.png as proposed by Daniel and we also put a comment with an example path to a folder inside the program file folder.

Maybe we should show the functions to call in order to retrieve the path(s) to the folders where the app is installed.

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.

Something like:

std::filesystem::path exePath{ GetPathToCurrentExe() };
auto iconPath{ exePath / "displayicon.png" };

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.

Sample should show use of MRT or another resource loader indirection to fetch that display string, too.

Comment thread specs/AppNotifications/AppNotifications-spec.md Outdated
Comment thread specs/AppNotifications/AppNotifications-spec.md Outdated
Comment thread test/TestApps/ToastNotificationsTestApp/main.cpp Outdated

## Registering for App Notifications using assets

For Unpackaged applications, the developer can opt to call into Register API with DisplayName and Icon.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"can Register using a custom DisplayName and Icon"?

Comment thread specs/AppNotifications/AppNotifications-spec.md
Comment thread test/TestApps/ToastNotificationsTestApp/main.cpp
Comment thread test/TestApps/ToastNotificationsTestApp/main.cpp
Comment thread dev/AppNotifications/AppNotificationManager.cpp
Comment thread dev/AppNotifications/AppNotificationManager.cpp
Comment thread dev/AppNotifications/AppNotificationManager.cpp
Comment thread dev/AppNotifications/ShellLocalization.cpp
Comment thread dev/AppNotifications/AppNotificationManager.cpp
}

void AppNotificationManager::Register()
void AppNotificationManager::RegisterHelper(hstring const& displayName, std::wstring const& iconFilePath)
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.

Why is one argument hstring and the other wstring? We should be consistent

{
THROW_HR_IF_MSG(E_ILLEGAL_METHOD_CALL, AppModel::Identity::IsPackagedProcess(), "Not applicable for packaged applications");

THROW_HR_IF(E_INVALIDARG, (displayName == winrt::hstring{}) || (iconUri == nullptr));
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.

Same issue with iconUri. If it is null, don't check. Let it crash further along. You should instead check for the validity of the uri's content: eg. empty path, invalid path etc.

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.

Atleast make sure that the uri is a valid file path and the file exists on disk.

THROW_HR_IF(E_INVALIDARG, (displayName == winrt::hstring{}) || (iconUri == nullptr));

std::wstring iconFilePath{ iconUri.Path() };
iconFilePath = iconFilePath.substr(1, iconFilePath.size() - 1);
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.

what is this substring for? Can you please explain this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

use RawUri()

std::wstring iconFilePath{ iconUri.Path() };
iconFilePath = iconFilePath.substr(1, iconFilePath.size() - 1);
// Uri converts backward slashes to forward slashes
std::replace(iconFilePath.begin(), iconFilePath.end(), '/', '\\');
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.

You are actually converting forward slash to back slash. So the comment is incorrect. https://newbedev.com/difference-between-forward-slash-and-backslash-in-file-path

std::wstring iconFilePath{ iconUri.Path() };
iconFilePath = iconFilePath.substr(1, iconFilePath.size() - 1);
// Uri converts backward slashes to forward slashes
std::replace(iconFilePath.begin(), iconFilePath.end(), '/', '\\');
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.

This conversion is extremely fragile. We should not have to do it. Have you checked why we were erroring out with forward slash in the path? We should confirm why we see this behavior before making this change.


void AppNotificationManager::Register()
{
RegisterHelper(winrt::hstring{} /* displayName */, std::wstring{} /* iconFilePath */);
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.

Why don't you retrieve the correct displayname and icon path here before calling into the helper? Passing in empty is a strange pattern.

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.

Also Register would fail now because you call empty. Please don't use anti patterns and use valid arguments to pass into
APIs

else
{
registeredClsid = RegisterComActivatorGuidAndAssets();
registeredClsid = RegisterComActivatorGuidAndAssets(winrt::hstring{} /* displayName */, std::wstring{} /* iconUri */);
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.

This is incorrect too. Don't pass empty arguments please. Instead pass in the overloaded arguments or the one from shortcut/process

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.

Actually why do we need to re-register assets at all? Aren't we simply re-routing to another process? @pmpurifoy ? Can you explain this one please and why we need to re-register assets as part of this flow?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually what really needs to happen here is we need to fetch the registered Guid and nothing more. But I will wait for Paul to chip in here :)

std::wstring registeredGuid;
auto hr = GetActivatorGuid(registeredGuid);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Call GetActivatorGuid

{
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", nullptr);

winrt::AppNotificationManager::Default().UnregisterAll();
Copy link
Copy Markdown
Contributor

@loneursid loneursid May 5, 2022

Choose a reason for hiding this comment

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

A better pattern in this situation, would be to call UnregisterAll() from scope_exit. It would

  1. make it instantly clear that UnregisterAll isn't under test here.
  2. though it doesn't apply to this specific case as Unregister can't throw E_INVALIDARG, avoid a situation where the fuction under test does not throw as expected but the cleanup one does. The test would then pass although it should be failing.

This comment applies to all similar tests

}

bool VerifyRegisterWithDisplayNameAndIcon_Unpackaged()
{
Copy link
Copy Markdown
Contributor

@loneursid loneursid May 5, 2022

Choose a reason for hiding this comment

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

It's a shame that the only validation we do in this test is that Register() didn't throw. I wish we could verify that calling register actually did something useful and valid, i.e.: besides running code that didn't crash / throw...

}

TEST_METHOD(VerifyRegisterWithAssetsFail)
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I assume that we run a single test for packaged just to confirm that register works for packaged, and that we rely on the more complete suite of unpackaged tests to actually verify Register()'s actual behavior.

It would be nice if, we could just run the suite twice: once for unpackaged and one for packaged. It seems the only difference is whether RunTestUnpackaged or RunTest is used to launch the test. It feels like dependency injection could help here.

Not suggesting you do it now, that's probably beyond the point of this PR but that could be a nice future refactor.

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.

It's possible to detect at compile time if you're built with WindowsAppSDKSelfContained=true or false. At least, I think it is now in Foundation with recent changes I made to .props/.targets

Our test infrastructure does tend to build everything both ways though it was only running SelfContained=false. My PR in the Aggregator fixes that (going in soon).

Sorry limited network access at the moment. If you grip for SelfContained across build* you should find the C++ and C# symbols code can check for.
Though I suggest not messing with that until my changes go in as that has significant updates to the plumbing

{
THROW_HR(HRESULT_FROM_WIN32(GetLastError()));
}
winrt::check_bool(DeleteFile(iconFilePath.c_str()) != FALSE);
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: DeleteFileW is preferable to DeleteFile (ensures no ambiguity to the reader)

Is winrt::check_bool() equivalent wil::RETURN_IF_WIN32_BOOL_FALSE()?

NOTE: It's weird bordering on perverse to explicitly check for == or != for values of TRUE or FALSE

}

void AppNotificationManager::Register()
void AppNotificationManager::RegisterHelper(hstring const& displayName, std::wstring const& iconFilePath)
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.

Helper? 🤨

void Register();

// For Unpackaged apps only, the caller process will be registered as the COM server.
void Register(String displayName, Windows.Foundation.Uri iconUri);
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.

What happens when a Packaged app calls this? Is that an error?

Copy link
Copy Markdown
Member

@jonwis jonwis left a comment

Choose a reason for hiding this comment

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

See note about enhancing the samples.

@sharath2727
Copy link
Copy Markdown
Contributor Author

This PR will be closed and all the changes are now tracked on: #2596

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Notifications Toast notification, badges, Live Tiles, push notifications needs-triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants