패키지된 데스크톱 앱과 패키지되지 않은 데스크톱 앱은 UWP(유니버설 Windows 플랫폼) 앱과 마찬가지로 대화형 app 알림을 보낼 수 있습니다. 여기에는 패키지된 앱(패키지된 WinUI 3 데스크톱app에 대한 새 프로젝트 만들기 참조), 외부 위치가 있는 패키지된 앱(외부 위치로 패키징하여 패키지 ID 부여 참조) 및 패키지되지 않은 앱(패키지되지 않은 WinUI 3 데스크톱app에 대한 새 프로젝트 만들기 참조)이 포함됩니다.
그러나 패키지되지 않은 데스크톱 app의 경우 몇 가지 특별한 단계가 있습니다. 이는 다양한 활성화 체계와 런타임에 패키지 ID가 없기 때문입니다.
Note
"toast 알림"이라는 용어가 "app 알림"으로 대체되고 있습니다. 이러한 용어는 모두 Windows의 동일한 기능을 참조하지만 시간이 지남에 따라 설명서에서 "toast 알림"의 사용을 단계적으로 중단합니다.
1단계: Windows SDK 사용
Windows SDK app를 사용하도록 설정하지 않은 경우 먼저 사용하도록 설정해야 합니다. 몇 가지 주요 단계가 있습니다.
-
runtimeobject.lib에 를 추가합니다. - Windows SDK를 대상으로 합니다.
프로젝트를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.
위쪽 구성 메뉴에서 모든 구성을 선택하여, 다음 변경 사항이 디버그와 릴리스 모두에 적용되도록 합니다.
링커 -> 입력에서 runtimeobject.lib을 추가 종속성에 추가합니다.
그런 다음 일반에서 Windows SDK 버전이 버전 10.0 이상으로 설정되어 있는지 확인합니다.
2단계: 호환성 라이브러리 코드 복사
DesktopNotificationManagerCompat.h 파일과 DesktopNotificationManagerCompat.cpp 파일을 GitHub에서 프로젝트로 복사합니다. 호환 라이브러리는 데스크톱 알림의 복잡성을 대부분 추상화합니다. 다음 지침에는 호환성 라이브러리가 필요합니다.
미리 컴파일된 헤더를 사용하는 경우, DesktopNotificationManagerCompat.cpp 파일의 첫 번째 줄에 #include "stdafx.h"을 포함하세요.
3단계: 헤더 파일 및 네임스페이스 포함
호환 라이브러리 헤더 파일 및 Windows 알림 API 사용과 관련된 헤더 파일 및 네임스페이스를 포함합니다.
#include "DesktopNotificationManagerCompat.h"
#include <NotificationActivationCallback.h>
#include <windows.ui.notifications.h>
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::UI::Notifications;
using namespace Microsoft::WRL;
4단계: 활성화기 구현
사용자가 귀하의 알림을 클릭할 때 app이(가) 작업을 수행할 수 있도록 알림 app 활성화에 대한 처리기를 구현해야 합니다. 알림 센터에서 알림이 유지되도록 하기 위해 이 작업이 필요합니다(app이 닫혀 있을 때도 알림이 며칠 후에 클릭될 수 있기 때문에). 이 클래스는 프로젝트의 모든 위치에 배치할 수 있습니다.
UUID를 포함하여 아래와 같이 INotificationActivationCallback 인터페이스를 구현하고, CoCreatableClass를 호출하여 클래스를 COM에서 생성할 수 있도록 플래그를 지정합니다. UUID의 경우 많은 온라인 GUID 생성기 중 하나를 사용하여 고유한 GUID를 만듭니다. 이 GUID CLSID(클래스 식별자)는 Action Center가 COM 활성화할 클래스를 아는 방법입니다.
// The UUID CLSID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public:
virtual HRESULT STDMETHODCALLTYPE Activate(
_In_ LPCWSTR appUserModelId,
_In_ LPCWSTR invokedArgs,
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
ULONG dataCount) override
{
// TODO: Handle activation
}
};
// Flag class as COM creatable
CoCreatableClass(NotificationActivator);
5단계: 알림 플랫폼에 등록
그런 다음 알림 플랫폼에 등록해야 합니다. 패키징된 경우와 비패키징된 경우에 따라 app 적용할 단계가 다릅니다. 둘 다 지원하는 경우 두 단계 집합을 모두 수행해야 합니다(그러나 라이브러리가 이를 처리하므로 코드를 포크할 필요가 없음).
Packaged
app 패키지된 경우(패키지된 WinUI 3 데스크톱app에 대한 새 프로젝트 만들기 참조) 또는 외부 위치로 패키지된 경우(외부 위치로 패키징하여 패키지 ID 부여 참조) 또는 둘 다를 지원하는 경우 Package.appxmanifest에서 다음을 추가합니다.
- xmlns:com에 대한 선언
- xmlns:desktop 선언
- IgnorableNamespaces 속성에서 com과 데스크톱.
-
4단계에서의 GUID를 사용하여 COM 활성화자를 위한 com:Extension.
Arguments="-ToastActivated"알림에서 시작되었음을 알 수 있도록 app을 반드시 포함하십시오. - desktop:Extension을 windows.toastNotificationActivation에 대한 확장으로 사용하여 알림 활성화자 CLSID(4단계의 GUID)를 선언합니다app.
Package.appxmanifest
<Package
...
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
IgnorableNamespaces="... com desktop">
...
<Applications>
<Application>
...
<Extensions>
<!--Register COM CLSID LocalServer32 registry key-->
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="YourProject\YourProject.exe" Arguments="-ToastActivated" DisplayName="Toast activator">
<com:Class Id="replaced-with-your-guid-C173E6ADF0C3" DisplayName="Toast activator"/>
</com:ExeServer>
</com:ComServer>
</com:Extension>
<!--Specify which CLSID to activate when toast clicked-->
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" />
</desktop:Extension>
</Extensions>
</Application>
</Applications>
</Package>
Unpackaged
app 패키지가 해제된 경우(패키지되지 않은 WinUI 3 데스크톱app에 대한 새 프로젝트 만들기 참조) 또는 둘 다 지원하는 경우 시작의 바로 가기에서 AUMID(애플리케이션 사용자 모델 ID) 및 toast 활성화자 CLSID(4단계의 GUID)를 선언해야 합니다app.
고유한 AUMID를 선택하여 app를 식별합니다. 이는 일반적으로 [CompanyName].[AppName] 형식입니다. 그러나 모든 앱에서 고유한지 확인하려고 합니다(따라서 마지막에 일부 숫자를 자유롭게 추가하세요).
5.1단계: WiX 설치 관리자
설치 관리자에 WiX를 사용하는 경우 아래와 같이 Product.wxs 파일을 편집하여 시작 메뉴 바로 가기에 두 개의 바로 가기 속성을 추가합니다. 4단계의 GUID는 아래에 보이는 것처럼 {} 안에 넣어야 합니다.
Product.wxs
<Shortcut Id="ApplicationStartMenuShortcut" Name="Wix Sample" Description="Wix Sample" Target="[INSTALLFOLDER]WixSample.exe" WorkingDirectory="INSTALLFOLDER">
<!--AUMID-->
<ShortcutProperty Key="System.AppUserModel.ID" Value="YourCompany.YourApp"/>
<!--COM CLSID-->
<ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{replaced-with-your-guid-C173E6ADF0C3}"/>
</Shortcut>
Important
정상적으로 디버깅하기 전에 app를 설치 관리자를 통해 한 번 설치하여 AUMID 및 CLSID가 포함된 시작 바로 가기가 생성되도록 해야 합니다. 시작 바로 가기가 있으면 Visual Studio에서 F5를 사용하여 디버그할 수 있습니다.
5.2단계: AUMID 및 COM 서버 등록
그런 다음 설치 관리자에 관계없이 '의 시작 코드(알림 API를 호출하기 전에)에서 appRegisterAumidAndComServer 메서드를 호출하여 4단계에서 알림 활성화자 클래스를 지정하고 위에서 사용한 AUMID를 지정합니다.
// Register AUMID and COM server (for a packaged app, this is a no-operation)
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"YourCompany.YourApp", __uuidof(NotificationActivator));
app 패키지된 배포와 패키지되지 않은 배포를 모두 지원하는 경우 관계없이 이 메서드를 자유롭게 호출할 수 있습니다. 패키지(즉, 런타임 시 패키지 ID 사용)를 실행하는 경우 이 메서드는 즉시 반환됩니다. 코드를 포크할 필요가 없습니다.
이 메서드를 사용하면 AUMID를 지속적으로 제공하지 않고도 호환 API를 호출하여 알림을 보내고 관리할 수 있습니다. 또한 COM 서버에 대한 LocalServer32 레지스트리 키를 삽입합니다.
6단계: COM 활성화자 등록
패키지된 앱과 패키지되지 않은 앱 모두 toast 활성화를 처리할 수 있도록 알림 활성화기 유형을 등록해야 합니다.
app'의 시작 코드에서 다음 RegisterActivator 메서드를 호출합니다. toast 활성화를 받으려면 반드시 이 함수를 호출해야 합니다.
// Register activator type
hr = DesktopNotificationManagerCompat::RegisterActivator();
7단계: 알림 보내기
알림 보내기는 UWP 앱과 동일하지만, DesktopNotificationManagerCompat를 사용하여 ToastNotifier를 생성하는 것이 예외입니다. 호환 라이브러리는 패키지된 앱과 패키지되지 않은 앱 간의 차이를 자동으로 처리하므로 코드를 포크할 필요가 없습니다. 패키지 app되지 않은 경우 컴파일 라이브러리는 RegisterAumidAndComServer 를 호출할 때 제공한 AUMID를 캐시하므로 AUMID를 제공하거나 제공하지 않을 때 걱정할 필요가 없습니다.
레거시 Windows 8.1 알림 템플릿이 4단계에서 만든 COM 알림 활성화기를 활성화하지 않으므로 아래와 같이 toast 바인딩을 사용해야 합니다.
Important
Http 이미지는 매니페스트에 인터넷 기능이 있는 패키지된 앱에서만 지원됩니다. 패키지되지 않은 앱은 http 이미지를 지원하지 않습니다. 이미지를 로컬 데이터에 다운로드하고 로컬 app 로 참조해야 합니다.
// Construct XML
ComPtr<IXmlDocument> doc;
hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(
L"<toast><visual><binding template='ToastGeneric'><text>Hello world</text></binding></visual></toast>",
&doc);
if (SUCCEEDED(hr))
{
// See full code sample to learn how to inject dynamic text, buttons, and more
// Create the notifier
// Desktop apps must use the compat method to create the notifier.
ComPtr<IToastNotifier> notifier;
hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier);
if (SUCCEEDED(hr))
{
// Create the notification itself (using helper method from compat library)
ComPtr<IToastNotification> toast;
hr = DesktopNotificationManagerCompat::CreateToastNotification(doc.Get(), &toast);
if (SUCCEEDED(hr))
{
// And show it!
hr = notifier->Show(toast.Get());
}
}
}
Important
데스크톱 앱은 레거시 toast 템플릿(예: ToastText02)을 사용할 수 없습니다. COM CLSID를 지정하면 레거시 템플릿의 활성화가 실패합니다. 위와 같이 Windows ToastGeneric 템플릿을 사용해야 합니다.
8단계: 활성화 처리
사용자가 알림 또는 알림의 단추를 클릭하면 appNotificationActivator 클래스의 Activate 메서드가 호출됩니다.
Activate 메서드 내에서 알림에서 지정한 인수를 해석하여, 사용자가 입력하거나 선택한 데이터를 가져온 후, 그에 따라 app를 활성화할 수 있습니다.
Note
Activate 메서드는 주 스레드와 별도의 스레드에서 호출됩니다.
// The GUID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public:
virtual HRESULT STDMETHODCALLTYPE Activate(
_In_ LPCWSTR appUserModelId,
_In_ LPCWSTR invokedArgs,
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
ULONG dataCount) override
{
std::wstring arguments(invokedArgs);
HRESULT hr = S_OK;
// Background: Quick reply to the conversation
if (arguments.find(L"action=reply") == 0)
{
// Get the response user typed.
// We know this is first and only user input since our toasts only have one input
LPCWSTR response = data[0].Value;
hr = DesktopToastsApp::SendResponse(response);
}
else
{
// The remaining scenarios are foreground activations,
// so we first make sure we have a window open and in foreground
hr = DesktopToastsApp::GetInstance()->OpenWindowIfNeeded();
if (SUCCEEDED(hr))
{
// Open the image
if (arguments.find(L"action=viewImage") == 0)
{
hr = DesktopToastsApp::GetInstance()->OpenImage();
}
// Open the app itself
// User might have clicked on app title in Action Center which launches with empty args
else
{
// Nothing to do, already launched
}
}
}
if (FAILED(hr))
{
// Log failed HRESULT
}
return S_OK;
}
~NotificationActivator()
{
// If we don't have window open
if (!DesktopToastsApp::GetInstance()->HasWindow())
{
// Exit (this is for background activation scenarios)
exit(0);
}
}
};
// Flag class as COM creatable
CoCreatableClass(NotificationActivator);
app가 닫혀 있는 상태에서도 제대로 실행되도록 지원하려면, WinMain 함수 내에서 app 알림으로부터 실행되었는지를 확인하는 것이 필요합니다. 알림에서 실행되는 경우 시작 인수로 "-ToastActivated"가 제공됩니다. 이 경우에는 일반 시작 활성화 코드를 수행하는 것을 중지하고, 필요한 경우 NotificationActivator이 창 실행을 처리하도록 해야 합니다.
// Main function
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLineArgs, _In_ int)
{
RoInitializeWrapper winRtInitializer(RO_INIT_MULTITHREADED);
HRESULT hr = winRtInitializer;
if (SUCCEEDED(hr))
{
// Register AUMID and COM server (for a packaged app, this is a no-operation)
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"WindowsNotifications.DesktopToastsCpp", __uuidof(NotificationActivator));
if (SUCCEEDED(hr))
{
// Register activator type
hr = DesktopNotificationManagerCompat::RegisterActivator();
if (SUCCEEDED(hr))
{
DesktopToastsApp app;
app.SetHInstance(hInstance);
std::wstring cmdLineArgsStr(cmdLineArgs);
// If launched from toast
if (cmdLineArgsStr.find(TOAST_ACTIVATED_LAUNCH_ARG) != std::string::npos)
{
// Let our NotificationActivator handle activation
}
else
{
// Otherwise launch like normal
app.Initialize(hInstance);
}
app.RunMessageLoop();
}
}
}
return SUCCEEDED(hr);
}
이벤트 활성화 시퀀스
활성화 시퀀스는 다음과 입니다.
현재 app가 이미 실행 중이라면:
- NotificationActivator 활성화
귀하의 app가 실행 중이지 않은 경우:
- EXE app이 실행되었으며, "-ToastActivated"라는 명령줄 인수를 받습니다.
- NotificationActivator 활성화
전경 및 백그라운드 활성화
데스크톱 앱의 경우 전경 및 백그라운드 활성화가 동일하게 처리됩니다. 사용자의 COM 활성화자가 호출됩니다. 창을 표시할지 아니면 단순히 일부 작업을 수행한 다음 종료할지 결정하는 것은 사용자의 app코드에 달려 있습니다. 따라서 알림 콘텐츠에 백그라운드의 app을 지정해도 동작은 변경되지 않습니다.
9단계: 알림 제거 및 관리
알림 제거 및 관리는 UWP 앱과 동일합니다. 그러나 데스크톱용 AUMID를 제공하는 것에 대해 걱정할 필요가 없도록 호환 라이브러리를 사용하여 app을 가져오는 것이 좋습니다.
std::unique_ptr<DesktopNotificationHistoryCompat> history;
auto hr = DesktopNotificationManagerCompat::get_History(&history);
if (SUCCEEDED(hr))
{
// Remove a specific toast
hr = history->Remove(L"Message2");
// Clear all toasts
hr = history->Clear();
}
10단계: 배포 및 디버깅
패키지된 데스크톱을 배포하고 디버그하려면 app 참조하세요.app
데스크톱 app을 배포하고 디버그하려면 AUMID 및 CLSID가 포함된 시작 바로 가기를 활성화하기 위해, 정상적으로 디버깅을 시작하기 전에 한 번 설치 관리자를 사용하여 app을(를) 설치해야 합니다. 시작 바로 가기가 있으면 Visual Studio에서 F5를 사용하여 디버그할 수 있습니다.
알림이 데스크톱 app 에 표시되지 않고 예외가 발생하지 않는 경우, 시작 메뉴 바로 가기가 없거나(설치 관리자를 통해 app 설치하십시오), 코드에서 사용한 AUMID가 시작 메뉴 바로 가기의 AUMID와 일치하지 않음을 의미합니다.
알림이 표시되지만 알림 센터에서 유지되지 않는 경우(팝업이 해제된 후 사라짐) COM 활성화기를 올바르게 구현하지 않았음을 의미합니다.
패키지된 데스크톱과 패키지되지 않은 데스크톱 app을 모두 설치한 경우, 패키지된 app이 활성화를 처리할 때 패키지되지 않은 app 데스크톱을 대체합니다 toast. 즉, 패키지화되지 않은 app의 알림을 클릭하면, 패키지 app가 실행됩니다. 패키지화된 app를 제거할 경우 활성화가 다시 비패키지화된 app로 되돌아갑니다.
수신 HRESULT 0x800401f0 CoInitialize has not been called.를 받으면, API를 호출하기 전에 CoInitialize(nullptr)에서 app를 확실히 호출하십시오.
Compat API를 호출하는 동안 HRESULT 0x8000000e A method was called at an unexpected time.를 받았다면, 이는 필요한 Register 메서드를 호출하지 않았음을 의미합니다. (또는 패키지된 app의 경우, 현재 app가 패키지된 컨텍스트에서 실행되고 있지 않을 수 있습니다.)
unresolved external symbol 컴파일 오류가 많이 발생하는 경우, 1단계에서 runtimeobject.lib에 을 추가하는 것을 잊었을 가능성이 높습니다. 이는 디버그 구성에만 추가하고 릴리스 구성에는 추가하지 않았기 때문일 수도 있습니다.
이전 버전의 Windows 처리
Windows 8.1 이하를 지원하는 경우, DesktopNotificationManagerCompat API를 호출하거나 ToastGeneric 토스트 메시지를 보내기 전에 런타임에서 Windows에서 실행 중인지 확인하는 것이 좋습니다.
Windows 8에서는 toast 알림을 도입했지만 ToastText01 과 같은 레거시 toast 템플릿을 사용했습니다. 알림은 지속되지 않은 간단한 팝업이므로 ToastNotification 클래스의 메모리 내 Activated 이벤트에 의해 활성화가 처리되었습니다. Windows 10에서는대화형 ToastGeneric 알림
| OS | ToastGeneric | COM 활성화 도구 | 레거시 toast 템플릿 |
|---|---|---|---|
| Windows 10 이상 | Supported | Supported | 지원됨(COM 서버를 활성화하지 않음) |
| Windows 8.1/8 | N/A | N/A | Supported |
| Windows 7 이하 | N/A | N/A | N/A |
Windows 10 이상에서 실행 중인지 확인하려면 헤더를 <VersionHelpers.h> 포함하고 IsWindows10OrGreater 메서드를 확인합니다. 반환 true되는 경우 이 설명서에 설명된 모든 메서드를 계속 호출합니다.
#include <VersionHelpers.h>
if (IsWindows10OrGreater())
{
// Running on Windows 10 or later, continue with sending toasts!
}
알려진 문제
수정됨: App이(가) toast 클릭 후 포커스가 맞춰지지 않습니다. 빌드 15063 이하에서는 COM 서버를 활성화할 때 포그라운드 권한이 애플리케이션으로 전송되지 않았습니다. 따라서 당신의 app을(를) 전경으로 이동하려고 할 때 단순히 깜박일 것입니다. 이 문제에 대한 해결 방법이 없습니다. 빌드 16299 이상에서 이 작업을 해결했습니다.
Resources
Windows developer