添加帮助程序类型和函数
在此步骤中,我们会添加一些帮助程序类型和函数,代码的其余部分会使用它们。 就在 main
之前添加以下内容。
struct prop_variant : PROPVARIANT
{
prop_variant() noexcept : PROPVARIANT{}
{
}
~prop_variant() noexcept
{
clear();
}
void clear() noexcept
{
WINRT_VERIFY_(S_OK, ::PropVariantClear(this));
}
};
struct registry_traits
{
using type = HKEY;
static void close(type value) noexcept
{
WINRT_VERIFY_(ERROR_SUCCESS, ::RegCloseKey(value));
}
static constexpr type invalid() noexcept
{
return nullptr;
}
};
using registry_key = winrt::handle_type<registry_traits>;
std::wstring get_module_path()
{
std::wstring path(100, L'?');
uint32_t path_size{};
DWORD actual_size{};
do
{
path_size = static_cast<uint32_t>(path.size());
actual_size = ::GetModuleFileName(nullptr, path.data(), path_size);
if (actual_size + 1 > path_size)
{
path.resize(path_size * 2, L'?');
}
} while (actual_size + 1 > path_size);
path.resize(actual_size);
return path;
}
std::wstring get_shortcut_path()
{
std::wstring format{ LR"(%ProgramData%\Microsoft\Windows\Start Menu\Programs\)" };
format += (this_app_name + L".lnk");
auto required{ ::ExpandEnvironmentStrings(format.c_str(), nullptr, 0) };
std::wstring path(required - 1, L'?');
::ExpandEnvironmentStrings(format.c_str(), path.data(), required);
return path;
}
实现其余函数和 wmain 入口点函数
删除 main 函数,在其原位置粘贴此代码清单,其中包括用于注册组件类,然后提供能够回调应用程序的 toast 的代码。
void register_callback()
{
DWORD registration{};
winrt::check_hresult(::CoRegisterClassObject(
callback_guid,
make<callback_factory>().get(),
CLSCTX_LOCAL_SERVER,
REGCLS_SINGLEUSE,
®istration));
}
void create_shortcut()
{
auto link{ winrt::create_instance<IShellLink>(CLSID_ShellLink) };
std::wstring module_path{ get_module_path() };
winrt::check_hresult(link->SetPath(module_path.c_str()));
auto store = link.as<IPropertyStore>();
prop_variant value;
winrt::check_hresult(::InitPropVariantFromString(this_app_name.c_str(), &value));
winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ID, value));
value.clear();
winrt::check_hresult(::InitPropVariantFromCLSID(callback_guid, &value));
winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ToastActivatorCLSID, value));
auto file{ store.as<IPersistFile>() };
std::wstring shortcut_path{ get_shortcut_path() };
winrt::check_hresult(file->Save(shortcut_path.c_str(), TRUE));
std::wcout << L"In " << shortcut_path << L", created a shortcut to " << module_path << std::endl;
}
void update_registry()
{
std::wstring key_path{ LR"(SOFTWARE\Classes\CLSID\{????????-????-????-????-????????????})" };
::StringFromGUID2(callback_guid, key_path.data() + 23, 39);
key_path += LR"(\LocalServer32)";
registry_key key;
winrt::check_win32(::RegCreateKeyEx(
HKEY_CURRENT_USER,
key_path.c_str(),
0,
nullptr,
0,
KEY_WRITE,
nullptr,
key.put(),
nullptr));
::RegDeleteValue(key.get(), nullptr);
std::wstring path{ get_module_path() };
winrt::check_win32(::RegSetValueEx(
key.get(),
nullptr,
0,
REG_SZ,
reinterpret_cast<BYTE const*>(path.c_str()),
static_cast<uint32_t>((path.size() + 1) * sizeof(wchar_t))));
std::wcout << L"In " << key_path << L", registered local server at " << path << std::endl;
}
void create_toast()
{
XmlDocument xml;
std::wstring toastPayload
{
LR"(
<toast>
<visual>
<binding template='ToastGeneric'>
<text>)"
};
toastPayload += this_app_name;
toastPayload += LR"(
</text>
</binding>
</visual>
<actions>
<action content='Call back )";
toastPayload += this_app_name;
toastPayload += LR"(
' arguments='the_args' activationKind='Foreground' />
</actions>
</toast>)";
xml.LoadXml(toastPayload);
ToastNotification toast{ xml };
ToastNotifier notifier{ ToastNotificationManager::CreateToastNotifier(this_app_name) };
notifier.Show(toast);
::Sleep(50); // Give the callback chance to display.
}
void LaunchedNormally(HANDLE, INPUT_RECORD &, DWORD &);
void LaunchedFromNotification(HANDLE, INPUT_RECORD &, DWORD &);
int wmain(int argc, wchar_t * argv[], wchar_t * /* envp */[])
{
winrt::init_apartment();
register_callback();
HANDLE consoleHandle{ ::GetStdHandle(STD_INPUT_HANDLE) };
INPUT_RECORD buffer{};
DWORD events{};
::FlushConsoleInputBuffer(consoleHandle);
if (argc == 1)
{
LaunchedNormally(consoleHandle, buffer, events);
}
else if (argc == 2 && wcscmp(argv[1], L"-Embedding") == 0)
{
LaunchedFromNotification(consoleHandle, buffer, events);
}
}
void LaunchedNormally(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
try
{
bool runningAsAdmin{ ::IsUserAnAdmin() == TRUE };
std::wcout << this_app_name << L" is running" << (runningAsAdmin ? L" (administrator)." : L" (NOT as administrator).") << std::endl;
if (runningAsAdmin)
{
create_shortcut();
update_registry();
}
std::wcout << std::endl << L"Press 'T' to display a toast notification (press any other key to exit)." << std::endl;
::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
if (towupper(buffer.Event.KeyEvent.uChar.UnicodeChar) == L'T')
{
create_toast();
}
}
catch (winrt::hresult_error const& e)
{
std::wcout << L"Error: " << e.message().c_str() << L" (" << std::hex << std::showbase << std::setw(8) << static_cast<uint32_t>(e.code()) << L")" << std::endl;
}
}
void LaunchedFromNotification(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
::Sleep(50); // Give the callback chance to display its message.
std::wcout << std::endl << L"Press any key to exit." << std::endl;
::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
}
注意,到这里程序大概完成了,接下来的文章里会使用这个程序做一些测试。