Update the features about Notification and Cert.

This commit is contained in:
Bruce
2025-11-12 14:59:34 +08:00
parent a5d8981528
commit ce5d3af63b
25 changed files with 866 additions and 86 deletions

View File

@@ -10,8 +10,13 @@ BOOL APIENTRY DllMain( HMODULE hModule,
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
CoInitializeEx (NULL, COINIT_MULTITHREADED);
RoInitialize (RO_INIT_MULTITHREADED);
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
CoUninitialize ();
RoUninitialize ();
break;
}
return TRUE;

View File

@@ -6,10 +6,15 @@
#include "raii.h"
#include "version.h"
#include "nstring.h"
#ifdef GetFullPathName
#undef GetFullPathName
#endif
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
std::wstring GetFullPathName (const std::wstring &lpFileName)
{
if (lpFileName.empty ()) return L"";
@@ -36,8 +41,29 @@ 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)
Windows::Data::Xml::Dom::XmlDocument ^TemplateToastNoticeXml (const std::wstring &lpTemplateName)
{
std::wnstring temp = lpTemplateName;
try
{
using toastttype = Windows::UI::Notifications::ToastTemplateType;
auto tttype = Windows::UI::Notifications::ToastTemplateType::ToastImageAndText01;
if (temp.equals (L"ToastText01")) tttype = toastttype::ToastText01;
else if (temp.equals (L"ToastText02")) tttype = toastttype::ToastText02;
else if (temp.equals (L"ToastText03")) tttype = toastttype::ToastText03;
else if (temp.equals (L"ToastText04")) tttype = toastttype::ToastText04;
else if (temp.equals (L"ToastImageAndText01")) tttype = toastttype::ToastImageAndText01;
else if (temp.equals (L"ToastImageAndText02")) tttype = toastttype::ToastImageAndText02;
else if (temp.equals (L"ToastImageAndText03")) tttype = toastttype::ToastImageAndText03;
else if (temp.equals (L"ToastImageAndText04")) tttype = toastttype::ToastImageAndText04;
else tttype = (toastttype)-1;
if ((INT16)tttype > 0)
{
auto tt = Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent (tttype);
if (tt) return tt;
}
}
catch (...) {}
auto xmldoc = ref new Windows::Data::Xml::Dom::XmlDocument ();
auto root = xmldoc->CreateElement ("toast");
xmldoc->AppendChild (root);
@@ -45,7 +71,6 @@ LPWSTR GetToastNoticeXml (LPCWSTR lpTemplateName)
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");
@@ -149,23 +174,21 @@ LPWSTR GetToastNoticeXml (LPCWSTR lpTemplateName)
auto text1 = xmldoc->CreateElement ("text");
binding->AppendChild (text1);
}
return xmldoc;
}
LPWSTR GetToastNoticeXml (LPCWSTR lpTemplateName)
{
auto xmldoc = TemplateToastNoticeXml (lpTemplateName);
return _wcsdup (xmldoc->GetXml ()->Data ());
}
LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath)
Windows::Data::Xml::Dom::XmlDocument ^SimpleToastNoticeXml (const std::wstring &lpText, const std::wstring &lpImgPath = L"")
{
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""));
Windows::Data::Xml::Dom::XmlDocument ^xmldoc = nullptr;
std::wnstring img = lpImgPath;
std::wstring text = std::wnstring::trim (std::wstring (lpText));
{
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 ()));
// std::wstring xmltemplate = L"<toast><visual><binding template='ToastGeneric'><text></text></binding></visual></toast>";
xmldoc = TemplateToastNoticeXml (img.empty () ? L"ToastText01" : L"ToastImageAndText01");
}
Windows::Foundation::Uri ^imguri = nullptr;
try { imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ())); }
@@ -173,8 +196,8 @@ LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath)
{
try
{
std::wstring fullpath = GetFullPathName (lpImagePath ? lpImagePath : L"");
if (fullpath.empty ()) fullpath = lpImagePath ? lpImagePath : L"";
std::wstring fullpath = GetFullPathName (lpImgPath);
if (fullpath.empty ()) fullpath = lpImgPath;
imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ()));
}
catch (...) { imguri = nullptr; }
@@ -199,34 +222,32 @@ LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath)
node->SetAttribute (L"alt", ref new Platform::String (L"image"));
}
}
return xmldoc;
}
LPWSTR GenerateSimpleToastNoticeXml (LPCWSTR lpText, LPCWSTR lpImagePath)
{
auto xmldoc = SimpleToastNoticeXml (lpText ? lpText : L"", lpImagePath ? lpImagePath : L"");
return _wcsdup (xmldoc->GetXml ()->Data ());
}
LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImagePath)
Windows::Data::Xml::Dom::XmlDocument ^SimpleToastNoticeXml2 (const std::wstring &lpTitle, const std::wstring &lpText = L"", const std::wstring &lpImagePath = L"")
{
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 ();
Windows::Data::Xml::Dom::XmlDocument ^xmldoc = nullptr;
std::wnstring img = lpImagePath;
std::wstring title = std::wnstring (lpTitle).trim ();
std::wstring text = std::wnstring (lpText).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 xmltemplate = L"<toast><visual><binding template='ToastGeneric'><text></text></binding></visual></toast>";
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; // 图 + 标题 + 正文
case 1: templatename = L"ToastText01"; break; // 仅正文
case 3: templatename = L"ToastText02"; break; // 标题 + 正文
case 5: templatename = L"ToastImageAndText01"; break; // 图 + 正文
case 7: 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 ()));
xmldoc = TemplateToastNoticeXml (templatename);
}
Windows::Foundation::Uri ^imguri = nullptr;
try { imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ())); }
@@ -234,8 +255,8 @@ LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR l
{
try
{
std::wstring fullpath = GetFullPathName (lpImagePath ? lpImagePath : L"");
if (fullpath.empty ()) fullpath = lpImagePath ? lpImagePath : L"";
std::wstring fullpath = GetFullPathName (lpImagePath);
if (fullpath.empty ()) fullpath = lpImagePath;
imguri = ref new Windows::Foundation::Uri (ref new Platform::String (img.c_str ()));
}
catch (...) { imguri = nullptr; }
@@ -271,6 +292,11 @@ LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR l
node->SetAttribute (L"alt", ref new Platform::String (L"image"));
}
}
return xmldoc;
}
LPWSTR GenerateSimpleToastNoticeXml2 (LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImagePath)
{
auto xmldoc = SimpleToastNoticeXml2 (lpTitle ? lpTitle : L"", lpText ? lpText : L"", lpImagePath ? lpImagePath : L"");
return _wcsdup (xmldoc->GetXml ()->Data ());
}
// 会实时记录 hResult;
@@ -299,7 +325,7 @@ catch (...) \
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)
HRESULT CreateToastNoticeFromXml (const std::wstring &lpIdName, Windows::Data::Xml::Dom::XmlDocument ^pIXml, NOTICE_ACTIVECALLBACK pfCallback = nullptr, void *pCustom = nullptr, LPWSTR *lpExceptMsg = nullptr)
{
using XmlDoc = Windows::Data::Xml::Dom::XmlDocument;
using ToastNotification = Windows::UI::Notifications::ToastNotification;
@@ -312,10 +338,9 @@ HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString,
try
{
Windows::UI::Notifications::ToastNotifier ^notifier = nullptr;
if (lpIdName && *lpIdName) notifier = ToastMgr::CreateToastNotifier (ref new String (lpIdName));
if (!std::wnstring (lpIdName).empty ()) notifier = ToastMgr::CreateToastNotifier (ref new String (lpIdName.c_str ()));
else notifier = ToastMgr::CreateToastNotifier ();
auto xmldoc = ref new XmlDoc ();
xmldoc->LoadXml (ref new String (lpXmlString));
auto &xmldoc = pIXml;
auto toast = ref new Toast (xmldoc);
toast->Activated += ref new Windows::Foundation::TypedEventHandler <
Windows::UI::Notifications::ToastNotification ^,
@@ -324,11 +349,208 @@ HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString,
if (pfCallback) pfCallback (pCustom);
});
notifier->Show (toast);
return S_OK;
return hr = S_OK;
}
catch_lasterr (&hr, lpExceptMsg);
return hr;
}
HRESULT CreateToastNoticeFromXml (const std::wstring &lpIdName, const std::wstring &lpXmlString, NOTICE_ACTIVECALLBACK pfCallback = nullptr, void *pCustom = nullptr, LPWSTR *lpExceptMsg = nullptr)
{
using XmlDoc = Windows::Data::Xml::Dom::XmlDocument;
using String = Platform::String;
using Object = Platform::Object;
if (lpExceptMsg) *lpExceptMsg = nullptr;
auto &hr = g_lasthr;
try
{
auto xmldoc = ref new XmlDoc ();
xmldoc->LoadXml (ref new String (lpXmlString.c_str ()));
return hr = CreateToastNoticeFromXml (lpIdName, xmldoc, pfCallback, pCustom, lpExceptMsg);
}
catch_lasterr (&hr, lpExceptMsg);
return hr;
}
HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
return CreateToastNoticeFromXml (lpIdName ? lpIdName : L"", lpXmlString ? lpXmlString : L"", pfCallback, pCustom, lpExceptMsg);
}
HRESULT CreateToastNotice2 (LPCWSTR lpIdName, LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImgPath, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
auto &hr = g_lasthr;
try
{
auto xmldoc = SimpleToastNoticeXml2 (lpTitle ? lpTitle : L"", lpText ? lpText : L"", lpImgPath ? lpImgPath : L"");
return hr = CreateToastNoticeFromXml (lpIdName, xmldoc, pfCallback, pCustom, lpExceptMsg);
}
catch_lasterr (&hr, lpExceptMsg);
return hr;
}
HRESULT CreateToastNotice (LPCWSTR lpIdName, LPCWSTR lpText, LPCWSTR lpImgPath, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
return CreateToastNotice2 (lpIdName, lpText, nullptr, lpImgPath, pfCallback, pCustom, lpExceptMsg);
}
std::wstring GetRandomText (size_t length = 16, const std::wstring &charset = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
{
if (charset.empty ()) return L"";
static bool seeded = false;
if (!seeded)
{
std::srand ((unsigned int)(std::time (nullptr) ^ (uintptr_t)&seeded));
seeded = true;
}
std::wstring ret;
ret.reserve (length);
for (size_t i = 0; i < length; ++i)
{
size_t index = (size_t)(std::rand () % charset.size ());
ret.push_back (charset [index]);
}
return ret;
}
std::wstring CombinePath (const std::wstring &left, const std::wstring &right)
{
std::vector <WCHAR> buf (left.capacity () + right.capacity () + 2);
PathCombineW (buf.data (), left.c_str (), right.c_str ());
return buf.data ();
}
std::wstring GetRamdomFileName (const std::wstring &directory, const std::wstring &ext = L".tmp", bool retfullpath = true)
{
std::wnstring name = std::wnstring::trim (GetRandomText (3) + L"-" + GetRandomText (5) + L"-" + GetRandomText (3)) + std::wnstring::trim (ext);
std::wnstring path = CombinePath (directory, name);
while (GetFileAttributesW (path.c_str ()) != INVALID_FILE_ATTRIBUTES)
{
name = GetRandomText (3) + L"-" + GetRandomText (5) + L"-" + GetRandomText (3) + ext;
path = CombinePath (directory, name);
}
if (retfullpath) return path;
else return name;
}
std::wstring GetTempDirectory ()
{
std::vector <WCHAR> ret (GetTempPathW (0, nullptr) + 2);
GetTempPathW (ret.size (), ret.data ());
return ret.data ();
}
std::wstring GetRamdomTempFileName (const std::wstring &ext = L".tmp", bool retfullpath = true)
{
return GetRamdomFileName (GetTempDirectory (), ext, retfullpath);
}
std::wstring IStreamToTempFile (IStream *p, const std::wstring &ext = L".tmp")
{
if (!p) throw ref new Platform::InvalidArgumentException ("IStream is nullptr.");
std::wnstring outpath = GetRamdomTempFileName (ext);
auto &stream = p;
STATSTG stat;
HRESULT hr = stream->Stat (&stat, STATFLAG_NONAME);
if (FAILED (hr)) throw Platform::Exception::CreateException (hr);
LARGE_INTEGER liZero = {};
stream->Seek (liZero, STREAM_SEEK_SET, nullptr);
HANDLE hFile = CreateFileW (outpath.c_str (), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
const size_t bufsize = 4096;
BYTE buf [bufsize] = {0};
ULONG bytesRead = 0;
DWORD bytesWritten = 0;
while (true)
{
hr = stream->Read (buf, bufsize, &bytesRead);
if (FAILED (hr)) { CloseHandle (hFile); throw Platform::Exception::CreateException (hr); }
if (bytesRead == 0) break;
if (!WriteFile (hFile, buf, bytesRead, &bytesWritten, nullptr) || bytesWritten != bytesRead)
{
CloseHandle (hFile);
throw std::runtime_error ("WriteFile failed.");
}
}
CloseHandle (hFile);
return outpath;
}
void DeleteFileThreadSafe (LPWSTR filepath)
{
raii endt ([&] () {
if (filepath) free (filepath);
filepath = nullptr;
});
Sleep (5000);
DeleteFileW (filepath);
}
HRESULT CreateToastNoticeWithIStream2 (LPCWSTR lpIdName, LPCWSTR lpTitle, LPCWSTR lpText, HANDLE pIImgStream, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
auto &hr = g_lasthr;
try
{
std::wnstring imgpath = L"";
try
{
if (pIImgStream)
{
IStream *img = (IStream *)pIImgStream;
LARGE_INTEGER li;
li.QuadPart = 0;
img->Seek (li, STREAM_SEEK_SET, nullptr);
STATSTG stat;
hr = img->Stat (&stat, STATFLAG_DEFAULT);
if (SUCCEEDED (hr) && stat.pwcsName)
{
if (stat.pwcsName) imgpath = stat.pwcsName;
CoTaskMemFree (stat.pwcsName);
}
else
{
imgpath = IStreamToTempFile (img, L".jpg");
CreateThread (nullptr, 0, (LPTHREAD_START_ROUTINE)DeleteFileThreadSafe, _wcsdup (imgpath.c_str ()), 0, nullptr);
}
}
Windows::Foundation::Uri ^uri = ref new Windows::Foundation::Uri (ref new Platform::String (imgpath.c_str ()));
}
catch (...) { imgpath = L""; }
return hr = CreateToastNotice2 (lpIdName, lpTitle, lpText, imgpath.c_str (), pfCallback, pCustom, lpExceptMsg);
}
catch_lasterr (&hr, lpExceptMsg);
return hr;
}
HRESULT CreateToastNoticeWithIStream (LPCWSTR lpIdName, LPCWSTR lpText, HANDLE pIImgStream, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg)
{
return CreateToastNoticeWithIStream2 (lpIdName, lpText, nullptr, pIImgStream, pfCallback, pCustom, lpExceptMsg);
}
HRESULT NoticeGetLastHResult () { return g_lasthr; }
LPCWSTR NoticeGetLastDetailMessage () { return g_lastexc.c_str (); }
HRESULT CreateShortcutWithAppIdW (LPCWSTR pszShortcutPath, LPCWSTR pszTargetPath, LPCWSTR pszAppId)
{
HRESULT hr;
if (FAILED (hr)) return hr;
IShellLinkW *pShellLinkW = nullptr;
raii reltask1 ([&] () {
if (pShellLinkW) pShellLinkW->Release ();
pShellLinkW = nullptr;
});
hr = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void **)&pShellLinkW);
if (FAILED (hr)) return hr;
hr = pShellLinkW->SetPath (pszTargetPath); return hr;
IPropertyStore *pPropStore = nullptr;
raii reltask2 ([&] () {
if (pPropStore) pPropStore->Release ();
pPropStore = nullptr;
});
hr = pShellLinkW->QueryInterface (IID_IPropertyStore, (void **)&pPropStore);
if (SUCCEEDED (hr))
{
PROPVARIANT propvar;
hr = InitPropVariantFromString (pszAppId, &propvar);
if (SUCCEEDED (hr))
{
hr = pPropStore->SetValue (PKEY_AppUserModel_ID, propvar);
if (SUCCEEDED (hr)) hr = pPropStore->Commit ();
PropVariantClear (&propvar);
}
}
else pPropStore = nullptr;
IPersistFile *pPersistFile = nullptr;
raii reltask3 ([&] () {
if (pPersistFile) pPersistFile->Release ();
pPersistFile = nullptr;
});
hr = pShellLinkW->QueryInterface (IID_IPersistFile, (void **)&pPersistFile);
if (SUCCEEDED (hr)) hr = pPersistFile->Save (pszShortcutPath, TRUE);
else pPersistFile = nullptr;
return hr;
}

View File

@@ -50,7 +50,27 @@ extern "C"
// pCustom 可以传入自定义内容并在回调中使用
// lpExceptMsg 返回异常信息。获取到的指针必须由 free 释放。
NOTICE_API HRESULT CreateToastNoticeFromXmlDocument (LPCWSTR lpIdName, LPCWSTR lpXmlString, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
// 创建一个简单的 Toast 通知。仅支持一段文本和一张图片(图片若不需要则设置为 NULL 或空文本)
// 一些参数作用与 CreateToastNoticeFromXmlDocument 中的同名参数作用一致。
NOTICE_API HRESULT CreateToastNotice (LPCWSTR lpIdName, LPCWSTR lpText, LPCWSTR lpImgPath, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
// 创建一个简单的 Toast 通知。支持两段文本和一张图片(图片若不需要则设置为 NULL 或空文本)
// lpText 可以设置为 NULL 或空文本。此时函数的作用与 CreateToastNotice 一致。
// 一些参数作用与 CreateToastNoticeFromXmlDocument 中的同名参数作用一致。
NOTICE_API HRESULT CreateToastNotice2 (LPCWSTR lpIdName, LPCWSTR lpTitle, LPCWSTR lpText, LPCWSTR lpImgPath, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
// 创建一个简单的 Toast 通知。支持两段文本和一张图片(图片是 IStream 流,如果不想设置则置 NULL
// lpText 可以设置为 NULL 或空文本。此时函数的作用与 CreateToastNoticeWithIStream 一致。
// 一些参数作用与 CreateToastNoticeFromXmlDocument 中的同名参数作用一致。
NOTICE_API HRESULT CreateToastNoticeWithIStream2 (LPCWSTR lpIdName, LPCWSTR lpTitle, LPCWSTR lpText, HANDLE pIImgStream, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
// 创建一个简单的 Toast 通知。支持两段文本和一张图片(图片是 IStream 流,如果不想设置则置 NULL
// 一些参数作用与 CreateToastNoticeFromXmlDocument 中的同名参数作用一致。
NOTICE_API HRESULT CreateToastNoticeWithIStream (LPCWSTR lpIdName, LPCWSTR lpText, HANDLE pIImgStream, NOTICE_ACTIVECALLBACK pfCallback, void *pCustom, LPWSTR *lpExceptMsg);
// 获取上一次操作的 HReuslt注意返回的 HResult 不一定代表错误,因为这是记录每一个步骤的 HResult
NOTICE_API HRESULT NoticeGetLastHResult ();
// 获取上一次异常操作的错误信息。(注意:仅在发生异常时才会记录)
NOTICE_API LPCWSTR NoticeGetLastDetailMessage ();
// 创建快捷方式
// (不用安装程序原生的创建,因为需要 AppUserID 才能使用 Toast 通知,当然这个限制只有 Windows 8.x 有Windows 10 没有这个限制了)
NOTICE_API HRESULT CreateShortcutWithAppIdW (LPCWSTR pszShortcutPath, LPCWSTR pszTargetPath, LPCWSTR pszAppId);
#ifdef _DEFAULT_INIT_VALUE_
#undef _DEFAULT_INIT_VALUE_
#endif

BIN
notice/notice.rc Normal file

Binary file not shown.

View File

@@ -41,10 +41,10 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ReferencePath>$(VCINSTALLDIR)\vcpackages;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib;$(WindowsSdkDir)\References\CommonConfiguration\Neutral;$(ReferencePath)</ReferencePath>
<ReferencePath>$(VCINSTALLDIR)\vcpackages;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib;$(WindowsSdkDir)\References\CommonConfiguration\Neutral;$(VSSDK140Install)..\VC\atlmfc\lib;$(ReferencePath)</ReferencePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ReferencePath>$(VCINSTALLDIR)\vcpackages;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib;$(WindowsSdkDir)\References\CommonConfiguration\Neutral;$(ReferencePath)</ReferencePath>
<ReferencePath>$(VCINSTALLDIR)\vcpackages;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib;$(WindowsSdkDir)\References\CommonConfiguration\Neutral;$(VSSDK140Install)..\VC\atlmfc\lib;$(ReferencePath)</ReferencePath>
</PropertyGroup>
<!-- 关键:启用 WinRT 扩展 -->
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@@ -61,6 +61,7 @@
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -79,9 +80,11 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="notice.cpp" />
<ClCompile Include="stdafx.cpp" />
</ItemGroup>
@@ -89,9 +92,13 @@
<ClInclude Include="notice.h" />
<ClInclude Include="nstring.h" />
<ClInclude Include="raii.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="version.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="notice.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -21,6 +21,9 @@
<ClCompile Include="notice.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@@ -41,5 +44,13 @@
<ClInclude Include="nstring.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="notice.rc">
<Filter>资源文件</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -347,73 +347,73 @@ namespace std
{
bool default_upper = false, default_include_blank_in_str = false;
public:
using base = std::basic_string <ct, tr, al>;
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) {}
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
Base normalize (bool upper, bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, upper, includemidblank);
}
base normalize (bool upper) const
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
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
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
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); }
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
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
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); }
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)
{

14
notice/resource.h Normal file
View File

@@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by notice.rc
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -28,6 +28,10 @@
#include <shlobj.h>
#include <propkey.h>
#include <comdef.h>
#include <string>
#include <Shlwapi.h>
#include <algorithm>
#include <random>
#using <Windows.winmd>
using namespace Microsoft::WRL;
using namespace ABI::Windows::UI::Notifications;