Update Toast Notice Features

This commit is contained in:
Bruce
2025-11-10 23:24:00 +08:00
parent 978f9b6a45
commit a5d8981528
15 changed files with 848 additions and 30 deletions

View File

@@ -5,8 +5,11 @@
#include "notice.h"
#include "raii.h"
#include "version.h"
#include "nstring.h"
#ifdef GetFullPathName
#undef GetFullPathName
#endif
std::wstring GetFullPathName (const std::wstring &lpFileName)
{
if (lpFileName.empty ()) return L"";
@@ -18,9 +21,6 @@ std::wstring GetFullPathName (const std::wstring &lpFileName)
return std::wstring (buffer.data (), result);
}
std::wstring g_swExceptionCode = L"";
std::wstring g_swExceptionDetail = L"";
struct destruct
{
std::function <void ()> endtask = nullptr;
@@ -36,4 +36,299 @@ static std::wstring StringToWString (const std::string &str, UINT codePage = CP_
MultiByteToWideChar (codePage, 0, str.c_str (), -1, &wstr [0], len);
return wstr;
}
LPWSTR GetToastNoticeXml (LPCWSTR lpTemplateName)
{
auto xmldoc = ref new Windows::Data::Xml::Dom::XmlDocument ();
auto root = xmldoc->CreateElement ("toast");
xmldoc->AppendChild (root);
auto visual = xmldoc->CreateElement ("visual");
root->AppendChild (visual);
auto binding = xmldoc->CreateElement ("binding");
visual->AppendChild (binding);
std::wnstring temp = lpTemplateName ? lpTemplateName : L"";
if (temp.equals (L"ToastText01"))
{
binding->SetAttribute ("template", "ToastText01");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
binding->AppendChild (text1);
}
else if (temp.equals (L"ToastText02"))
{
binding->SetAttribute ("template", "ToastText02");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto text2 = xmldoc->CreateElement ("text");
text2->SetAttribute ("id", "2");
binding->AppendChild (text1);
binding->AppendChild (text2);
}
else if (temp.equals (L"ToastText03"))
{
binding->SetAttribute ("template", "ToastText03");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto text2 = xmldoc->CreateElement ("text");
text2->SetAttribute ("id", "2");
binding->AppendChild (text1);
binding->AppendChild (text2);
}
else if (temp.equals (L"ToastText04"))
{
binding->SetAttribute ("template", "ToastText04");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto text2 = xmldoc->CreateElement ("text");
text2->SetAttribute ("id", "2");
auto text3 = xmldoc->CreateElement ("text");
text3->SetAttribute ("id", "3");
binding->AppendChild (text1);
binding->AppendChild (text2);
binding->AppendChild (text3);
}
else if (temp.equals (L"ToastImageAndText01"))
{
binding->SetAttribute ("template", "ToastImageAndText01");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto img1 = xmldoc->CreateElement ("image");
img1->SetAttribute ("id", "1");
img1->SetAttribute ("src", "");
img1->SetAttribute ("alt", "");
binding->AppendChild (img1);
binding->AppendChild (text1);
}
else if (temp.equals (L"ToastImageAndText02"))
{
binding->SetAttribute ("template", "ToastImageAndText02");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto img1 = xmldoc->CreateElement ("image");
img1->SetAttribute ("id", "1");
img1->SetAttribute ("src", "");
img1->SetAttribute ("alt", "");
binding->AppendChild (img1);
binding->AppendChild (text1);
}
else if (temp.equals (L"ToastImageAndText03"))
{
binding->SetAttribute ("template", "ToastImageAndText03");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto text2 = xmldoc->CreateElement ("text");
text2->SetAttribute ("id", "2");
auto img1 = xmldoc->CreateElement ("image");
img1->SetAttribute ("id", "1");
img1->SetAttribute ("src", "");
img1->SetAttribute ("alt", "");
binding->AppendChild (img1);
binding->AppendChild (text1);
binding->AppendChild (text2);
}
else if (temp.equals (L"ToastImageAndText04"))
{
binding->SetAttribute ("template", "ToastImageAndText04");
auto text1 = xmldoc->CreateElement ("text");
text1->SetAttribute ("id", "1");
auto text2 = xmldoc->CreateElement ("text");
text2->SetAttribute ("id", "2");
auto text3 = xmldoc->CreateElement ("text");
text3->SetAttribute ("id", "3");
auto img1 = xmldoc->CreateElement ("image");
img1->SetAttribute ("id", "1");
img1->SetAttribute ("src", "");
img1->SetAttribute ("alt", "");
binding->AppendChild (img1);
binding->AppendChild (text1);
binding->AppendChild (text2);
binding->AppendChild (text3);
}
else
{
binding->SetAttribute ("template", "ToastImageAndText04");
auto text1 = xmldoc->CreateElement ("text");
binding->AppendChild (text1);
}
return _wcsdup (xmldoc->GetXml ()->Data ());
}
LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath)
{
auto xmldoc = ref new Windows::Data::Xml::Dom::XmlDocument ();
std::wnstring img = lpImagePath ? lpImagePath : L"";
std::wstring text = std::wnstring::trim (std::wstring (lpText ? lpText : L""));
{
std::wstring xmltemplate = L"<toast><visual><binding template='ToastGeneric'><text></text></binding></visual></toast>";
LPWSTR xt = nullptr;
raii relt ([&xt] () {
if (xt) free (xt);
xt = nullptr;
});
xt = GetToastNoticeXml (img.empty () ? L"ToastText01" : L"ToastImageAndText01");
if (xt && *xt) xmltemplate = xt;
xmldoc->LoadXml (ref new Platform::String (xmltemplate.c_str ()));
}
Windows::Foundation::Uri ^imguri = nullptr;
try { imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ())); }
catch (...)
{
try
{
std::wstring fullpath = GetFullPathName (lpImagePath ? lpImagePath : L"");
if (fullpath.empty ()) fullpath = lpImagePath ? lpImagePath : L"";
imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ()));
}
catch (...) { imguri = nullptr; }
}
auto toast = xmldoc->FirstChild;
auto visual = toast->FirstChild;
auto binding = visual->FirstChild;
auto binds = binding->ChildNodes;
auto textNodes = binding->SelectNodes (ref new Platform::String (L"text"));
if (textNodes && textNodes->Length > 0)
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (textNodes->Item (0));
if (node) node->InnerText = ref new Platform::String (text.c_str ());
}
auto imageNodes = binding->SelectNodes (ref new Platform::String (L"image"));
if (imageNodes && imageNodes->Length > 0 && !img.empty ())
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (imageNodes->Item (0));
if (node)
{
node->SetAttribute (L"src", ref new Platform::String (imguri && imguri->ToString ()->Data () ? imguri->ToString ()->Data () : img.c_str ()));
node->SetAttribute (L"alt", ref new Platform::String (L"image"));
}
}
return _wcsdup (xmldoc->GetXml ()->Data ());
}
LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImagePath)
{
auto xmldoc = ref new Windows::Data::Xml::Dom::XmlDocument ();
std::wnstring img = lpImagePath ? lpImagePath : L"";
std::wstring title = std::wnstring (lpTitle ? lpTitle : L"").trim ();
std::wstring text = std::wnstring (lpText ? lpText : L"").trim ();
{
std::wstring xmltemplate = L"<toast><visual><binding template='ToastGeneric'><text></text></binding></visual></toast>";
LPWSTR xt = nullptr;
raii relt ([&xt] () {
if (xt) free (xt);
xt = nullptr;
});
std::wstring templatename = L"";
WORD flag = (bool)(!img.empty ()) << 2 | (bool)title.size () << 1 | (bool)text.size ();
switch (flag)
{
case 0b001: templatename = L"ToastText01"; break; // 仅正文
case 0b011: templatename = L"ToastText02"; break; // 标题 + 正文
case 0b101: templatename = L"ToastImageAndText01"; break; // 图 + 正文
case 0b111: templatename = L"ToastImageAndText02"; break; // 图 + 标题 + 正文
default: templatename = L"ToastText01"; break;
}
xt = GetToastNoticeXml (templatename.c_str ());
if (xt && *xt) xmltemplate = xt;
xmldoc->LoadXml (ref new Platform::String (xmltemplate.c_str ()));
}
Windows::Foundation::Uri ^imguri = nullptr;
try { imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ())); }
catch (...)
{
try
{
std::wstring fullpath = GetFullPathName (lpImagePath ? lpImagePath : L"");
if (fullpath.empty ()) fullpath = lpImagePath ? lpImagePath : L"";
imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ()));
}
catch (...) { imguri = nullptr; }
}
auto binding = xmldoc->SelectSingleNode (ref new Platform::String (L"/toast/visual/binding"));
auto textNodes = binding->SelectNodes (ref new Platform::String (L"text"));
if (textNodes && textNodes->Length > 0)
{
unsigned int idx = 0;
if (!title.empty ())
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (textNodes->Item (idx++));
if (node) node->InnerText = ref new Platform::String (title.c_str ());
}
if (!text.empty () && idx < textNodes->Length)
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (textNodes->Item (idx));
if (node) node->InnerText = ref new Platform::String (text.c_str ());
}
else if (!text.empty () && title.empty ())
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (textNodes->Item (0));
if (node) node->InnerText = ref new Platform::String (text.c_str ());
}
}
auto imageNodes = binding->SelectNodes (ref new Platform::String (L"image"));
if (imageNodes && imageNodes->Length > 0 && !img.empty ())
{
auto node = dynamic_cast <Windows::Data::Xml::Dom::XmlElement ^> (imageNodes->Item (0));
if (node)
{
node->SetAttribute (L"src", ref new Platform::String (imguri && imguri->ToString ()->Data () ? imguri->ToString ()->Data () : img.c_str ()));
node->SetAttribute (L"alt", ref new Platform::String (L"image"));
}
}
return _wcsdup (xmldoc->GetXml ()->Data ());
}
// 会实时记录 hResult;
HRESULT g_lasthr = S_OK;
// 记录上一次异常(不会根据操作更新)的信息
std::wstring g_lastexc = L"";
#define catch_lasterr(_PHRESULT_Outptr_lphResult_, _HLPWSTR_Outptr_lpExcepMsg_) \
catch (Platform::Exception ^e) \
{ \
g_lastexc = e->ToString ()->Data (); \
if (_HLPWSTR_Outptr_lpExcepMsg_) *(_HLPWSTR_Outptr_lpExcepMsg_) = _wcsdup (g_lastexc.c_str ()); \
g_lasthr = SUCCEEDED ((HRESULT)e->HResult) ? E_FAIL : (HRESULT)e->HResult; \
if (_PHRESULT_Outptr_lphResult_) *(_PHRESULT_Outptr_lphResult_) = g_lasthr; \
} \
catch (const std::exception &e) \
{ \
g_lastexc = StringToWString (e.what () ? e.what () : "Unknown exception."); \
if (_HLPWSTR_Outptr_lpExcepMsg_) *(_HLPWSTR_Outptr_lpExcepMsg_) = _wcsdup (g_lastexc.c_str ()); \
g_lasthr = E_FAIL; \
if (_PHRESULT_Outptr_lphResult_) *(_PHRESULT_Outptr_lphResult_) = g_lasthr; \
} \
catch (...) \
{ \
g_lastexc = L"Unknown exception"; \
if (_HLPWSTR_Outptr_lpExcepMsg_) *(_HLPWSTR_Outptr_lpExcepMsg_) = _wcsdup (g_lastexc.c_str ()); \
g_lasthr = E_FAIL; \
if (_PHRESULT_Outptr_lphResult_) *(_PHRESULT_Outptr_lphResult_) = g_lasthr; \
}
HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
using XmlDoc = Windows::Data::Xml::Dom::XmlDocument;
using ToastNotification = Windows::UI::Notifications::ToastNotification;
using Toast = ToastNotification;
using ToastMgr = Windows::UI::Notifications::ToastNotificationManager;
using String = Platform::String;
using Object = Platform::Object;
auto &hr = g_lasthr;
if (lpExceptMsg) *lpExceptMsg = nullptr;
try
{
Windows::UI::Notifications::ToastNotifier ^notifier = nullptr;
if (lpIdName && *lpIdName) notifier = ToastMgr::CreateToastNotifier (ref new String (lpIdName));
else notifier = ToastMgr::CreateToastNotifier ();
auto xmldoc = ref new XmlDoc ();
xmldoc->LoadXml (ref new String (lpXmlString));
auto toast = ref new Toast (xmldoc);
toast->Activated += ref new Windows::Foundation::TypedEventHandler <
Windows::UI::Notifications::ToastNotification ^,
Platform::Object ^
> ([=] (Windows::UI::Notifications::ToastNotification ^sender, Platform::Object ^args) {
if (pfCallback) pfCallback (pCustom);
});
notifier->Show (toast);
return S_OK;
}
catch_lasterr (&hr, lpExceptMsg);
return hr;
}

View File

@@ -18,7 +18,7 @@ extern "C"
#endif
#ifdef __cplusplus
#define _DEFAULT_INIT_VALUE_(_init_value_) = _init_value_
#ifndef PKGMGR_EXPORTS
#ifndef NOTICE_EXPORTS
#define _DEFAULT_INIT_VALUE_FORFUNC_(_init_value_) = _init_value_
#else
#define _DEFAULT_INIT_VALUE_FORFUNC_(_init_value_)
@@ -27,6 +27,29 @@ extern "C"
#define _DEFAULT_INIT_VALUE_(_init_value_)
#define _DEFAULT_INIT_VALUE_FORFUNC_(_init_value_)
#endif
typedef void (*NOTICE_ACTIVECALLBACK) (void *pCustom);
// 参考https://learn.microsoft.com/zh-cn/previous-versions/windows/apps/hh761494(v=win.10)
// 通过 Toast 通知名来获取 XML 模板。
// 不符合会返回一个默认模板(只会有一个 text 节点。根据需要的话可以自己添加)
// 注意:返回的指针要自己 free 释放
NOTICE_API LPWSTR GetToastNoticeXml (LPCWSTR lpTemplateName);
// 获取一个简单的 Toast 通知 XML 文档。第一个参数是必须的。第二个参数为图片 URIfile:///)。
// 第二个参数如果为 NULL 或去掉首尾空的长度为 0 的文本则不会使用带图片的模板。
// 注意:返回的指针要自己通过 free 释放
NOTICE_API LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath);
// 获取一个简单的 Toast 通知 XML 文档。第一个参数是必须的。第三个参数为图片 URIfile:///)。
// 第三个参数如果为 NULL 或去掉首尾空的长度为 0 的文本则不会使用带图片的模板。
// 第二个参数可以为 NULL 或空文本。当为空时不会使用相关模板
// 注意:返回的指针要自己通过 free 释放
NOTICE_API LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImagePath);
// 创建并显示一个 Toast 通知
// 参数1 为非必须项,这意味着可以传入 NULL 或空文本。但是建议必须填。桌面应用
// 必须在开始菜单快捷方式储存处创建一个含有 AppUserModelID 的快捷方式才能使用 Toast 通知。而这个限制
// 在 Windows 10 已经去除。
// pfCallback 为点击 Toast 通知本体后触发的回调函数。注意:仅运行期才能用,且不一定会调用成功
// pCustom 可以传入自定义内容并在回调中使用
// lpExceptMsg 返回异常信息。获取到的指针必须由 free 释放。
NOTICE_API HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
#ifdef _DEFAULT_INIT_VALUE_
#undef _DEFAULT_INIT_VALUE_

View File

@@ -32,9 +32,7 @@
<CLRSupport>false</CLRSupport>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="..\packages\pugixml.1.15.0\build\native\pugixml.targets" Condition="Exists('..\packages\pugixml.1.15.0\build\native\pugixml.targets')" />
</ImportGroup>
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
@@ -89,19 +87,11 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="notice.h" />
<ClInclude Include="nstring.h" />
<ClInclude Include="raii.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="version.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\pugixml.1.15.0\build\native\pugixml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\pugixml.1.15.0\build\native\pugixml.targets'))" />
</Target>
</Project>

View File

@@ -38,8 +38,8 @@
<ClInclude Include="notice.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<ClInclude Include="nstring.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
</Project>

457
notice/nstring.h Normal file
View File

@@ -0,0 +1,457 @@
#pragma once
#include <string>
#include <locale>
#include <cctype>
namespace l0km
{
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> inline std::basic_string<E, TR, AL> toupper (const std::basic_string <E, TR, AL> &src)
{
std::basic_string <E, TR, AL> dst = src;
static const std::locale loc;
const std::ctype <E> &ctype = std::use_facet <std::ctype <E>> (loc);
for (typename std::basic_string <E, TR, AL>::size_type i = 0; i < src.size (); ++ i)
{
dst [i] = ctype.toupper (src [i]);
}
return dst;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> inline std::basic_string <E, TR, AL> tolower (const std::basic_string <E, TR, AL> &src)
{
std::basic_string <E, TR, AL> dst = src;
static const std::locale loc;
const std::ctype <E> &ctype = std::use_facet <std::ctype <E>> (loc);
for (typename std::basic_string <E, TR, AL>::size_type i = 0; i < src.size (); ++ i)
{
dst [i] = ctype.tolower (src [i]);
}
return dst;
}
inline char toupper (char ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <char>> (loc).toupper (ch);
}
inline char tolower (char ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <char>> (loc).tolower (ch);
}
inline wchar_t toupper (wchar_t ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <wchar_t>> (loc).toupper (ch);
}
inline wchar_t tolower (wchar_t ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <wchar_t>> (loc).tolower (ch);
}
inline int toupper (int ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <int>> (loc).toupper (ch);
}
inline int tolower (int ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <int>> (loc).tolower (ch);
}
}
template <typename ct> bool is_blank (ct &ch)
{
return ch == ct (' ') || ch == ct ('\t') || ch == ct ('\n');
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> std::basic_string <E, TR, AL> NormalizeString (const std::basic_string <E, TR, AL> &str, bool upper = false, bool includemidblank = false)
{
typedef std::basic_string <E, TR, AL> string_type;
string_type result;
if (str.empty ()) return result;
auto begin_it = str.begin ();
auto end_it = str.end ();
while (begin_it != end_it && is_blank (*begin_it)) ++begin_it;
while (end_it != begin_it && is_blank (*(end_it - 1))) --end_it;
bool in_space = false;
for (auto it = begin_it; it != end_it; ++ it)
{
if (is_blank (*it))
{
if (includemidblank)
{
if (!in_space)
{
result.push_back (E (' '));
in_space = true;
}
}
else
{
result.push_back (*it);
in_space = true;
}
}
else
{
result.push_back (*it);
in_space = false;
}
}
if (upper) return l0km::toupper (result);
else return l0km::tolower (result);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> bool IsNormalizeStringEquals (const std::basic_string <E, TR, AL> &l, const std::basic_string <E, TR, AL> &r, bool includemidblank = false)
{
auto _local_strlen = [] (const E *p) -> size_t {
size_t cnt = 0;
while (*(p + cnt)) { cnt ++; }
return cnt;
};
const E *pl = l.c_str ();
const E *pr = r.c_str ();
while (*pl && is_blank (*pl)) ++ pl;
while (*pr && is_blank (*pr)) ++ pr;
const E *el = l.c_str () + _local_strlen (l.c_str ());
const E *er = r.c_str () + _local_strlen (r.c_str ());
while (el > pl && is_blank (*(el - 1))) --el;
while (er > pr && is_blank (*(er - 1))) --er;
while (pl < el && pr < er)
{
if (includemidblank)
{
if (is_blank (*pl) && is_blank (*pr))
{
while (pl < el && is_blank (*pl)) ++pl;
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
else if (is_blank (*pl))
{
while (pl < el && is_blank (*pl)) ++pl;
continue;
}
else if (is_blank (*pr))
{
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
}
if (l0km::tolower (*pl) != l0km::tolower (*pr)) return false;
++ pl;
++ pr;
}
while (pl < el && is_blank (*pl)) ++ pl;
while (pr < er && is_blank (*pr)) ++ pr;
return pl == el && pr == er;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> int64_t NormalizeStringCompare (const std::basic_string <E, TR, AL> &l, const std::basic_string <E, TR, AL> &r, bool includemidblank = false)
{
auto _local_strlen = [] (const E *p) -> size_t {
size_t cnt = 0;
while (*(p + cnt)) { cnt ++; }
return cnt;
};
const E *pl = l.c_str ();
const E *pr = r.c_str ();
while (*pl && is_blank (*pl)) ++ pl;
while (*pr && is_blank (*pr)) ++ pr;
const E *el = l.c_str () + _local_strlen (l.c_str ());
const E *er = r.c_str () + _local_strlen (r.c_str ());
while (el > pl && is_blank (*(el - 1))) -- el;
while (er > pr && is_blank (*(er - 1))) -- er;
while (pl < el && pr < er)
{
if (includemidblank)
{
if (is_blank (*pl) && is_blank (*pr))
{
while (pl < el && is_blank (*pl)) ++pl;
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
else if (is_blank (*pl))
{
while (pl < el && is_blank (*pl)) ++pl;
continue;
}
else if (is_blank (*pr))
{
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
}
E chl = l0km::tolower (*pl);
E chr = l0km::tolower (*pr);
if (chl != chr) return (int64_t)chl - (int64_t)chr;
++ pl;
++ pr;
}
while (pl < el && is_blank (*pl)) ++ pl;
while (pr < er && is_blank (*pr)) ++ pr;
if (pl == el && pr == er) return 0;
if (pl == el) return -1;
if (pr == er) return 1;
return (int64_t)l0km::tolower (*pl) - (int64_t)l0km::tolower (*pr);
}
template <typename CharT> bool IsNormalizeStringEquals (const CharT *l, const CharT *r, bool includemidblank = false)
{
if (!l || !r) return l == r;
auto skip_blank = [] (const CharT *&p)
{
while (*p && is_blank (*p)) ++ p;
};
const CharT *p1 = l;
const CharT *p2 = r;
skip_blank (p1);
skip_blank (p2);
while (*p1 && *p2)
{
CharT ch1 = l0km::tolower (*p1);
CharT ch2 = l0km::tolower (*p2);
if (ch1 != ch2) return false;
++ p1;
++ p2;
if (includemidblank)
{
if (is_blank (*p1) || is_blank (*p2))
{
skip_blank (p1);
skip_blank (p2);
}
}
}
skip_blank (p1);
skip_blank (p2);
return *p1 == 0 && *p2 == 0;
}
template <typename CharT> int64_t NormalizeStringCompare (const CharT *l, const CharT *r, bool includemidblank = false)
{
if (!l || !r) return l ? 1 : (r ? -1 : 0);
auto skip_blank = [] (const CharT *&p)
{
while (*p && is_blank (*p)) ++ p;
};
const CharT *p1 = l;
const CharT *p2 = r;
skip_blank (p1);
skip_blank (p2);
while (*p1 && *p2)
{
CharT ch1 = l0km::tolower (*p1);
CharT ch2 = l0km::tolower (*p2);
if (ch1 != ch2) return (ch1 < ch2) ? -1 : 1;
++ p1;
++ p2;
if (includemidblank)
{
if (is_blank (*p1) || is_blank (*p2))
{
skip_blank (p1);
skip_blank (p2);
}
}
}
skip_blank (p1);
skip_blank (p2);
if (*p1 == 0 && *p2 == 0) return 0;
if (*p1 == 0) return -1;
return 1;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> bool IsNormalizeStringEmpty (const std::basic_string <E, TR, AL> &str)
{
return IsNormalizeStringEquals (str, std::basic_string <E, TR, AL> ());
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> std::basic_string <E, TR, AL> StringTrim (const std::basic_string <E, TR, AL> &str, bool includemidblank = false)
{
typedef std::basic_string <E, TR, AL> string_type;
typedef typename string_type::size_type size_type;
if (str.empty ()) return string_type ();
size_type first = 0;
size_type last = str.size ();
while (first < last && is_blank (str [first])) ++first;
while (last > first && is_blank (str [last - 1])) --last;
if (first == last) return string_type ();
string_type result;
result.reserve (last - first);
bool in_space = false;
for (size_type i = first; i < last; ++ i)
{
if (is_blank (str [i]))
{
if (includemidblank)
{
if (!in_space)
{
result.push_back (E (' '));
in_space = true;
}
}
else
{
result.push_back (str [i]);
in_space = true;
}
}
else
{
result.push_back (str [i]);
in_space = false;
}
}
return result;
}
template <typename E, typename TR = std::char_traits<E>, typename AL = std::allocator <E>> size_t GetNormalizeStringLength (const std::basic_string <E, TR, AL> &str, bool includemidblank = false)
{
typedef typename std::basic_string <E, TR, AL>::size_type size_type;
if (str.empty ()) return 0;
size_type first = 0, last = str.size ();
while (first < last && is_blank (str [first])) ++first;
while (last > first && is_blank (str [last - 1])) --last;
if (first == last) return 0;
size_t length = 0;
bool in_space = false;
for (size_type i = first; i < last; ++i)
{
if (is_blank (str [i]))
{
if (includemidblank)
{
if (!in_space)
{
++ length;
in_space = true;
}
}
else
{
++ length;
in_space = true;
}
}
else
{
++ length;
in_space = false;
}
}
return length;
}
namespace std
{
template <typename ct, typename tr = std::char_traits <ct>, typename al = std::allocator <ct>> class basic_nstring: public std::basic_string <ct, tr, al>
{
bool default_upper = false, default_include_blank_in_str = false;
public:
using base = std::basic_string <ct, tr, al>;
using derive = std::basic_nstring <ct, tr, al>;
using typename base::size_type;
using typename base::value_type;
using base::base;
basic_nstring (): base (), default_upper (false), default_include_blank_in_str (false) {}
basic_nstring (const ct *pStr): base (pStr), default_upper (false), default_include_blank_in_str (false) {}
basic_nstring (const base &str): base (str) {}
basic_nstring (base &&str): base (std::move (str)) {}
basic_nstring (const ct *data, size_type count): base (data, count), default_upper (false), default_include_blank_in_str (false) {}
template <std::size_t N> basic_nstring (const ct (&arr) [N]) : base (arr, N) {}
template <typename InputIt> basic_nstring (InputIt first, InputIt last): base (first, last), default_upper (false), default_include_blank_in_str (false) {}
bool upper_default () const { return this->default_upper; }
bool upper_default (bool value) { return this->default_upper = value; }
bool include_blank_in_str_middle () const { return this->default_include_blank_in_str; }
bool include_blank_in_str_middle (bool value) { return this->default_include_blank_in_str = value; }
base normalize (bool upper, bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, upper, includemidblank);
}
base normalize (bool upper) const
{
return this->normalize (upper, default_include_blank_in_str);
}
base normalize () const { return this->normalize (default_upper); }
base upper (bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, true, includemidblank);
}
base upper () const { return this->upper (default_include_blank_in_str); }
base lower (bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, false, includemidblank);
}
base lower () const { return this->lower (default_include_blank_in_str); }
base trim (bool includemidblank) const
{
return StringTrim <ct, tr, al> (*this, includemidblank);
}
base trim () const { return this->trim (default_include_blank_in_str); }
size_t length (bool includemidblank) const { return GetNormalizeStringLength (*this, includemidblank); }
size_t length () const { return length (default_include_blank_in_str); }
bool empty () const
{
return IsNormalizeStringEmpty (*this);
}
bool equals (const base &another, bool includemidblank) const
{
return IsNormalizeStringEquals <ct, tr, al> (*this, another, includemidblank);
}
bool equals (const base &another) const { return equals (another, default_include_blank_in_str); }
int64_t compare (const base &another, bool includemidblank) const
{
return NormalizeStringCompare <ct, tr, al> (*this, another, includemidblank);
}
int64_t compare (const base &another) const { return compare (another, default_include_blank_in_str); }
base &string () { return *this; }
base to_string (bool upper, bool includemidblank) const { return this->normalize (upper, includemidblank); }
base to_string (bool upper) const { return this->normalize (upper, default_include_blank_in_str); }
base to_string () const { return this->normalize (default_upper); }
bool operator == (const base &other) const { return equals (other, false); }
bool operator != (const base &other) const { return !equals (other, false); }
bool operator < (const base &other) const { return compare (other, false) < 0; }
bool operator > (const base &other) const { return compare (other, false) > 0; }
bool operator <= (const base &other) const { return compare (other, false) <= 0; }
bool operator >= (const base &other) const { return compare (other, false) >= 0; }
int64_t operator - (const base &other) const { return compare (other, false); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static bool equals (const std::basic_string <E> &l, const std::basic_string <E> &r, bool remove_mid_blank = false)
{
return IsNormalizeStringEquals <E, TR, AL> (l, r, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static int64_t compare (const std::basic_string <E> &l, const std::basic_string <E> &r, bool remove_mid_blank = false)
{
return NormalizeStringCompare <E, TR, AL> (l, r, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_string <E, TR, AL> normalize (const std::basic_string <E> &str, bool to_upper = false, bool remove_mid_blank = false)
{
return NormalizeString <E, TR, AL> (str, to_upper, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_string <E, TR, AL> trim (const std::basic_string <E> &str, bool remove_mid_blank = false)
{
return StringTrim <E, TR, AL> (str, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static size_t length (const std::basic_string <E> &str, bool remove_mid_blank = false)
{
return GetNormalizeStringLength <E, TR, AL> (str, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static bool empty (const std::basic_string <E> &str)
{
return IsNormalizeStringEmpty <E, TR, AL> (str);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> to_nstring (std::basic_string <E> &str) { return std::basic_nstring <E> (str); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> toupper (const std::basic_nstring <E, TR, AL> &str) { return l0km::toupper (str); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> tolower (const std::basic_nstring <E, TR, AL> &str) { return l0km::tolower (str); }
};
typedef basic_nstring <char> nstring;
typedef basic_nstring <wchar_t> wnstring;
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="pugixml" version="1.15.0" targetFramework="native" />
</packages>

View File

@@ -28,11 +28,8 @@
#include <shlobj.h>
#include <propkey.h>
#include <comdef.h>
#using <Windows.winmd>
using namespace Microsoft::WRL;
using namespace ABI::Windows::UI::Notifications;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace Microsoft::WRL::Wrappers;
#include <pugiconfig.hpp>
#include <pugixml.hpp>