mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-13 02:17:26 +10:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce9efcc43 | ||
|
|
80c38d95e9 | ||
|
|
0b535d1dd8 | ||
|
|
e2ff745949 | ||
|
|
fbcf85559e | ||
|
|
ed3da927cc | ||
|
|
225d0ba8f9 | ||
|
|
afaf16620a | ||
|
|
4883d13950 | ||
|
|
b094ddc5f9 | ||
|
|
a422105c61 | ||
|
|
96423b8918 | ||
|
|
ba131ff14b | ||
|
|
7d59f5ebfb | ||
|
|
95f3a4b09a | ||
|
|
728f982310 | ||
|
|
2ca236c291 | ||
|
|
8a22282191 | ||
|
|
935600a6d9 | ||
|
|
d3bf4b6207 | ||
|
|
aa09a0dcc2 | ||
|
|
cd8cc8cbc6 | ||
|
|
1bfbe09c6f |
@@ -2,7 +2,7 @@
|
||||
|
||||
*Originally* **[Classic Shell](http://www.classicshell.net)** *by [Ivo Beltchev](https://sourceforge.net/u/ibeltchev/profile/)*
|
||||
|
||||
[](https://github.com/Open-Shell/Open-Shell-Menu/releases) [](https://github.com/Open-Shell/Open-Shell-Menu/releases) [](https://ci.appveyor.com/project/passionate-coder/open-shell-menu/branch/master) [](https://gitq.com/passionate-coder/Classic-Start) [](https://gitter.im/open-shell/Lobby)
|
||||
[](https://github.com/Open-Shell/Open-Shell-Menu/releases) [](https://github.com/Open-Shell/Open-Shell-Menu/releases) [](https://ci.appveyor.com/project/passionate-coder/open-shell-menu/branch/master) [](https://gitq.com/passionate-coder/Classic-Start) [](https://gitter.im/open-shell/Lobby) [](https://discord.gg/7H6arr5)
|
||||
|
||||
[Home Page](https://open-shell.github.io/Open-Shell-Menu)
|
||||
[Discussion room](https://gitter.im/Open-Shell)
|
||||
|
||||
@@ -5,9 +5,9 @@ for other languages.
|
||||
The final files (installers, archives) are saved to the Setup\Final folder.
|
||||
|
||||
You need the following tools:
|
||||
Visual Studio 2017 (Community Edition is enough)
|
||||
Visual Studio 2019 (Community Edition is enough)
|
||||
- Desktop development with C++ workload
|
||||
- Windows 10 SDK (10.0.17134.0) for Desktop C++
|
||||
- Windows 10 SDK (10.0.19041.0) for Desktop C++
|
||||
- Visual C++ ATL support
|
||||
HTML Help Workshop
|
||||
WiX 3.7
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "FNVHash.h"
|
||||
#include "StringUtils.h"
|
||||
#include "Translations.h"
|
||||
#include "json.hpp"
|
||||
#include <wininet.h>
|
||||
#include <softpub.h>
|
||||
|
||||
@@ -141,30 +142,9 @@ LRESULT CProgressDlg::OnCancel( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL&
|
||||
|
||||
static bool g_bCheckingVersion;
|
||||
|
||||
static DWORD GetTimeStamp( const wchar_t *fname )
|
||||
{
|
||||
HANDLE h=CreateFile(fname,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
|
||||
if (h==INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
DWORD res=0;
|
||||
DWORD q;
|
||||
IMAGE_DOS_HEADER header;
|
||||
if (ReadFile(h,&header,sizeof(header),&q,NULL) && q==sizeof(header))
|
||||
{
|
||||
if (SetFilePointer(h,header.e_lfanew+8,NULL,FILE_BEGIN)!=INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
if (!ReadFile(h,&res,4,&q,NULL) || q!=4)
|
||||
res=0;
|
||||
}
|
||||
}
|
||||
CloseHandle(h);
|
||||
return res;
|
||||
}
|
||||
|
||||
enum TDownloadResult
|
||||
{
|
||||
DOWNLOAD_OK,
|
||||
DOWNLOAD_SAMETIME,
|
||||
DOWNLOAD_CANCEL,
|
||||
|
||||
// errors
|
||||
@@ -176,8 +156,7 @@ enum TDownloadResult
|
||||
|
||||
// Downloads a file
|
||||
// filename - returns the name of the downloaded file
|
||||
// timestamp - if not zero, it is compared to the timestamp of the file and returns DOWNLOAD_SAMETIME if the same (and buf will be empty)
|
||||
static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf, CString *pFilename, DWORD timestamp, bool bAcceptCached, CProgressDlg *pProgress, TSettingsComponent component )
|
||||
static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf, CString *pFilename, bool bAcceptCached, CProgressDlg *pProgress, TSettingsComponent component )
|
||||
{
|
||||
const wchar_t *compName=L"Open-Shell";
|
||||
switch (component)
|
||||
@@ -211,7 +190,7 @@ static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf,
|
||||
int time=GetTickCount();
|
||||
if (pProgress)
|
||||
pProgress->SetText(LoadStringEx(IDS_PROGRESS_CONNECT));
|
||||
HINTERNET hConnect=InternetConnect(hInternet,host,INTERNET_DEFAULT_HTTP_PORT,L"",L"",INTERNET_SERVICE_HTTP,0,0);
|
||||
HINTERNET hConnect=InternetConnect(hInternet,host,components.nPort,L"",L"",INTERNET_SERVICE_HTTP,0,0);
|
||||
if (hConnect)
|
||||
{
|
||||
if (pProgress && pProgress->IsCanceled())
|
||||
@@ -219,7 +198,7 @@ static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf,
|
||||
const wchar_t *accept[]={L"*/*",NULL};
|
||||
if (res==DOWNLOAD_OK)
|
||||
{
|
||||
HINTERNET hRequest=HttpOpenRequest(hConnect,L"GET",file,NULL,NULL,accept,bAcceptCached?0:INTERNET_FLAG_RELOAD,0);
|
||||
HINTERNET hRequest=HttpOpenRequest(hConnect,L"GET",file,NULL,NULL,accept,((components.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0)|(bAcceptCached?0:INTERNET_FLAG_RELOAD),0);
|
||||
if (hRequest)
|
||||
{
|
||||
if (pProgress && pProgress->IsCanceled())
|
||||
@@ -264,7 +243,7 @@ static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf,
|
||||
if (fileSize==0)
|
||||
pProgress->SetProgress(-1);
|
||||
}
|
||||
int CHUNK_SIZE=timestamp?1024:32768; // start with small chunk to verify the timestamp
|
||||
int CHUNK_SIZE=32768;
|
||||
DWORD size=0;
|
||||
buf.reserve(fileSize+CHUNK_SIZE);
|
||||
while (1)
|
||||
@@ -286,25 +265,6 @@ static TDownloadResult DownloadFile( const wchar_t *url, std::vector<char> &buf,
|
||||
size+=dwSize;
|
||||
if (pProgress && fileSize)
|
||||
pProgress->SetProgress(size*100/fileSize);
|
||||
if (timestamp && (size<sizeof(IMAGE_DOS_HEADER) || buf[0]!='M' || buf[1]!='Z'))
|
||||
{
|
||||
res=DOWNLOAD_FAIL;
|
||||
break;
|
||||
}
|
||||
if (timestamp && size>=sizeof(IMAGE_DOS_HEADER))
|
||||
{
|
||||
DWORD pos=((IMAGE_DOS_HEADER*)&buf[0])->e_lfanew+8;
|
||||
if (size>=pos+4)
|
||||
{
|
||||
if (timestamp==*(DWORD*)&buf[pos])
|
||||
{
|
||||
res=DOWNLOAD_SAMETIME;
|
||||
break;
|
||||
}
|
||||
timestamp=0;
|
||||
CHUNK_SIZE=32768;
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.resize(size);
|
||||
}
|
||||
@@ -358,6 +318,7 @@ struct VersionCheckParams
|
||||
TSettingsComponent component;
|
||||
tNewVersionCallback callback;
|
||||
CProgressDlg *progress;
|
||||
bool nightly = false;
|
||||
};
|
||||
|
||||
// 0 - fail, 1 - success, 2 - cancel
|
||||
@@ -377,80 +338,17 @@ static DWORD WINAPI ThreadVersionCheck( void *param )
|
||||
return 0;
|
||||
}
|
||||
DWORD curVersion=GetVersionEx(g_Instance);
|
||||
regKey.SetDWORDValue(L"LastUpdateVersion",curVersion);
|
||||
|
||||
// download file
|
||||
wchar_t fname[_MAX_PATH]=L"%ALLUSERSPROFILE%\\OpenShell";
|
||||
DoEnvironmentSubst(fname,_countof(fname));
|
||||
SHCreateDirectory(NULL,fname);
|
||||
PathAppend(fname,L"update.ver");
|
||||
|
||||
bool res=false;
|
||||
CString urlBase=LoadStringEx(IDS_VERSION_URL);
|
||||
bool res = false;
|
||||
VersionData data;
|
||||
data.Clear();
|
||||
if (data.Load(fname,false)==VersionData::LOAD_OK)
|
||||
|
||||
{
|
||||
if (!data.altUrl.IsEmpty())
|
||||
urlBase=data.altUrl;
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
if (GetFileAttributesEx(fname,GetFileExInfoStandard,&attr))
|
||||
{
|
||||
DWORD writeTime=(DWORD)(((((ULONGLONG)attr.ftLastWriteTime.dwHighDateTime)<<32)|attr.ftLastWriteTime.dwLowDateTime)/TIME_DIVISOR);
|
||||
if (curTime>writeTime && (curTime-writeTime)<TIME_PRECISION)
|
||||
{
|
||||
res=true; // the file is valid and less than an hour old, don't download again
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!res)
|
||||
{
|
||||
data.Clear();
|
||||
CString url;
|
||||
url.Format(L"%s%d.%d.%d.ver",urlBase,curVersion>>24,(curVersion>>16)&0xFF,curVersion&0xFFFF);
|
||||
auto load = params.nightly ? data.LoadNightly() : data.Load();
|
||||
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG,L"URL: %s",url);
|
||||
#endif
|
||||
|
||||
std::vector<char> buf;
|
||||
TDownloadResult download=DownloadFile(url,buf,NULL,GetTimeStamp(fname),false,params.progress,params.component);
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG,L"Download result: %d",download);
|
||||
#endif
|
||||
if (download==DOWNLOAD_CANCEL)
|
||||
{
|
||||
g_bCheckingVersion=false;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (download<DOWNLOAD_FIRST_ERROR)
|
||||
{
|
||||
if (download==DOWNLOAD_SAMETIME || SaveFile(fname,buf)==0)
|
||||
{
|
||||
if (download==DOWNLOAD_SAMETIME)
|
||||
{
|
||||
HANDLE h=CreateFile(fname,GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
|
||||
if (h!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SetFileTime(h,NULL,NULL,(FILETIME*)&curTimeL);
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
if (params.progress)
|
||||
{
|
||||
params.progress->SetText(LoadStringEx(IDS_PROGRESS_VERIFY));
|
||||
params.progress->SetProgress(-1);
|
||||
}
|
||||
VersionData::TLoadResult load=data.Load(fname,false);
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG,L"Load result: %d",load);
|
||||
#endif
|
||||
if (load==VersionData::LOAD_BAD_FILE)
|
||||
DeleteFile(fname);
|
||||
res=(load==VersionData::LOAD_OK);
|
||||
}
|
||||
}
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG, L"Load result: %d", load);
|
||||
#endif
|
||||
res = (load == VersionData::LOAD_OK);
|
||||
}
|
||||
|
||||
curTime+=(rand()*TIME_PRECISION)/(RAND_MAX+1)-(TIME_PRECISION/2); // add between -30 and 30 minutes to randomize access
|
||||
@@ -553,6 +451,21 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec
|
||||
params->callback=callback;
|
||||
params->progress=NULL;
|
||||
|
||||
// check the Update setting (uses the current value in the registry, not the one from memory
|
||||
{
|
||||
CRegKey regSettings, regSettingsUser, regPolicy, regPolicyUser;
|
||||
bool bUpgrade = OpenSettingsKeys(COMPONENT_SHARED, regSettings, regSettingsUser, regPolicy, regPolicyUser);
|
||||
|
||||
CSetting settings[] = {
|
||||
{L"Nightly",CSetting::TYPE_BOOL,0,0,0},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
settings[0].LoadValue(regSettings, regSettingsUser, regPolicy, regPolicyUser);
|
||||
|
||||
params->nightly = GetSettingBool(settings[0]);
|
||||
}
|
||||
|
||||
if (!owner)
|
||||
return ThreadVersionCheck(params);
|
||||
|
||||
@@ -583,57 +496,38 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD buildTime=0;
|
||||
{
|
||||
// skip the update if the update component is not found
|
||||
wchar_t path[_MAX_PATH];
|
||||
GetModuleFileName(_AtlBaseModule.GetModuleInstance(),path,_countof(path));
|
||||
PathRemoveFileSpec(path);
|
||||
PathAppend(path,L"Update.exe");
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
if (!GetFileAttributesEx(path,GetFileExInfoStandard,&attr))
|
||||
return 0;
|
||||
|
||||
buildTime=(DWORD)(((((ULONGLONG)attr.ftCreationTime.dwHighDateTime)<<32)|attr.ftCreationTime.dwLowDateTime)/TIME_DIVISOR); // in 0.01 hours
|
||||
}
|
||||
|
||||
ULONGLONG curTimeL;
|
||||
GetSystemTimeAsFileTime((FILETIME*)&curTimeL);
|
||||
DWORD curTime=(DWORD)(curTimeL/TIME_DIVISOR); // in 0.01 hours
|
||||
if (curTime-buildTime>24*365*TIME_PRECISION)
|
||||
return 0; // the build is more than a year old, don't do automatic updates
|
||||
|
||||
CRegKey regKey;
|
||||
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\OpenShell")!=ERROR_SUCCESS)
|
||||
regKey.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\OpenShell");
|
||||
|
||||
DWORD lastVersion;
|
||||
if (regKey.QueryDWORDValue(L"LastUpdateVersion",lastVersion)!=ERROR_SUCCESS)
|
||||
lastVersion=0;
|
||||
if (lastVersion==GetVersionEx(g_Instance))
|
||||
{
|
||||
DWORD lastTime;
|
||||
if (regKey.QueryDWORDValue(L"LastUpdateTime",lastTime)!=ERROR_SUCCESS)
|
||||
lastTime=0;
|
||||
if ((int)(curTime-lastTime)<168*TIME_PRECISION)
|
||||
return 0; // check weekly
|
||||
}
|
||||
DWORD lastTime;
|
||||
if (regKey.QueryDWORDValue(L"LastUpdateTime",lastTime)!=ERROR_SUCCESS)
|
||||
lastTime=0;
|
||||
if ((int)(curTime-lastTime)<168*TIME_PRECISION)
|
||||
return 0; // check weekly
|
||||
|
||||
// check the Update setting (uses the current value in the registry, not the one from memory
|
||||
bool nightly = false;
|
||||
{
|
||||
CRegKey regSettings, regSettingsUser, regPolicy, regPolicyUser;
|
||||
bool bUpgrade=OpenSettingsKeys(COMPONENT_SHARED,regSettings,regSettingsUser,regPolicy,regPolicyUser);
|
||||
|
||||
CSetting settings[]={
|
||||
{L"Update",CSetting::TYPE_BOOL,0,0,1},
|
||||
{L"Nightly",CSetting::TYPE_BOOL,0,0,0},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
settings[0].LoadValue(regSettings,regSettingsUser,regPolicy,regPolicyUser);
|
||||
settings[1].LoadValue(regSettings,regSettingsUser,regPolicy,regPolicyUser);
|
||||
|
||||
if (!GetSettingBool(settings[0]))
|
||||
return 0;
|
||||
return 0;
|
||||
nightly = GetSettingBool(settings[1]);
|
||||
}
|
||||
|
||||
VersionCheckParams *params=new VersionCheckParams;
|
||||
@@ -641,6 +535,7 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec
|
||||
params->component=component;
|
||||
params->callback=callback;
|
||||
params->progress=NULL;
|
||||
params->nightly=nightly;
|
||||
|
||||
g_bCheckingVersion=true;
|
||||
if (check==CHECK_AUTO_WAIT)
|
||||
@@ -848,6 +743,204 @@ void VersionData::Swap( VersionData &data )
|
||||
std::swap(languages,data.languages);
|
||||
}
|
||||
|
||||
std::vector<char> DownloadUrl(const wchar_t* url)
|
||||
{
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG, L"URL: %s", url);
|
||||
#endif
|
||||
|
||||
std::vector<char> buffer;
|
||||
TDownloadResult download = DownloadFile(url, buffer, nullptr, false, nullptr, COMPONENT_UPDATE);
|
||||
|
||||
#ifdef UPDATE_LOG
|
||||
LogToFile(UPDATE_LOG, L"Download result: %d", download);
|
||||
#endif
|
||||
|
||||
if (download != DOWNLOAD_OK)
|
||||
buffer.clear();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
VersionData::TLoadResult VersionData::Load()
|
||||
{
|
||||
Clear();
|
||||
|
||||
auto buf = DownloadUrl(L"https://api.github.com/repos/Open-Shell/Open-Shell-Menu/releases/latest");
|
||||
if (buf.empty())
|
||||
return LOAD_ERROR;
|
||||
|
||||
try
|
||||
{
|
||||
auto data = json::parse(buf.begin(), buf.end());
|
||||
|
||||
// skip prerelease versions
|
||||
if (data["prerelease"].get<bool>())
|
||||
return LOAD_BAD_VERSION;
|
||||
|
||||
// get version from tag name
|
||||
auto tag = data["tag_name"].get<std::string>();
|
||||
if (tag.empty())
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
int v1, v2, v3;
|
||||
if (sscanf_s(tag.c_str(), "v%d.%d.%d", &v1, &v2, &v3) != 3)
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
newVersion = (v1 << 24) | (v2 << 16) | v3;
|
||||
|
||||
// installer url
|
||||
std::string url;
|
||||
for (const auto& asset : data["assets"])
|
||||
{
|
||||
if (asset["name"].get<std::string>().find("OpenShellSetup") == 0)
|
||||
{
|
||||
url = asset["browser_download_url"].get<std::string>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
downloadUrl.Append(CA2T(url.c_str()));
|
||||
|
||||
// changelog
|
||||
auto body = data["body"].get<std::string>();
|
||||
if (!body.empty())
|
||||
{
|
||||
auto name = data["name"].get<std::string>();
|
||||
if (!name.empty())
|
||||
{
|
||||
news.Append(CA2T(name.c_str()));
|
||||
news.Append(L"\r\n\r\n");
|
||||
}
|
||||
|
||||
news.Append(CA2T(body.c_str()));
|
||||
news.Replace(L"\\n", L"\n");
|
||||
news.Replace(L"\\r", L"\r");
|
||||
}
|
||||
|
||||
return LOAD_OK;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return LOAD_BAD_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
VersionData::TLoadResult VersionData::LoadNightly()
|
||||
{
|
||||
Clear();
|
||||
|
||||
auto buf = DownloadUrl(L"https://ci.appveyor.com/api/projects/passionate-coder/open-shell-menu/branch/master");
|
||||
if (buf.empty())
|
||||
return LOAD_ERROR;
|
||||
|
||||
try
|
||||
{
|
||||
auto data = json::parse(buf.begin(), buf.end());
|
||||
auto build = data["build"];
|
||||
|
||||
// get version
|
||||
auto version = build["version"].get<std::string>();
|
||||
if (version.empty())
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
{
|
||||
int v1, v2, v3;
|
||||
if (sscanf_s(version.c_str(), "%d.%d.%d", &v1, &v2, &v3) != 3)
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
newVersion = (v1 << 24) | (v2 << 16) | v3;
|
||||
|
||||
if (newVersion <= GetVersionEx(g_Instance))
|
||||
return LOAD_OK;
|
||||
}
|
||||
|
||||
// artifact url
|
||||
{
|
||||
auto jobId = build["jobs"][0]["jobId"].get<std::string>();
|
||||
if (jobId.empty())
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
std::wstring jobUrl(L"https://ci.appveyor.com/api/buildjobs/");
|
||||
jobUrl += std::wstring(jobId.begin(), jobId.end());
|
||||
jobUrl += L"/artifacts";
|
||||
|
||||
buf = DownloadUrl(jobUrl.c_str());
|
||||
if (buf.empty())
|
||||
return LOAD_ERROR;
|
||||
|
||||
auto artifacts = json::parse(buf.begin(), buf.end());
|
||||
|
||||
std::string fileName;
|
||||
for (const auto& artifact : artifacts)
|
||||
{
|
||||
auto name = artifact["fileName"].get<std::string>();
|
||||
if (name.find("OpenShellSetup") == 0)
|
||||
{
|
||||
fileName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileName.empty())
|
||||
return LOAD_BAD_FILE;
|
||||
|
||||
auto artifactUrl(jobUrl);
|
||||
artifactUrl += L'/';
|
||||
artifactUrl += std::wstring(fileName.begin(), fileName.end());
|
||||
|
||||
downloadUrl = artifactUrl.c_str();
|
||||
}
|
||||
|
||||
// changelog
|
||||
news.Append(CA2T(version.c_str()));
|
||||
news.Append(L"\r\n\r\n");
|
||||
try
|
||||
{
|
||||
// use Github API to compare commit that actual version was built from (APPVEYOR_REPO_COMMIT)
|
||||
// and commit that AppVeyor version was built from (commitId)
|
||||
auto commitId = build["commitId"].get<std::string>();
|
||||
|
||||
std::wstring compareUrl(L"https://api.github.com/repos/Open-Shell/Open-Shell-Menu/compare/");
|
||||
compareUrl += _T(APPVEYOR_REPO_COMMIT);
|
||||
compareUrl += L"...";
|
||||
compareUrl += std::wstring(commitId.begin(), commitId.end());
|
||||
|
||||
buf = DownloadUrl(compareUrl.c_str());
|
||||
auto compare = json::parse(buf.begin(), buf.end());
|
||||
|
||||
// then use first lines (subjects) of commit messages as changelog
|
||||
auto commits = compare["commits"];
|
||||
for (const auto& commit : commits)
|
||||
{
|
||||
auto message = commit["commit"]["message"].get<std::string>();
|
||||
|
||||
auto pos = message.find('\n');
|
||||
if (pos != message.npos)
|
||||
message.resize(pos);
|
||||
|
||||
news.Append(L"- ");
|
||||
news.Append(CA2T(message.c_str()));
|
||||
news.Append(L"\r\n");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return LOAD_BAD_FILE;
|
||||
}
|
||||
|
||||
return LOAD_OK;
|
||||
}
|
||||
|
||||
VersionData::TLoadResult VersionData::Load( const wchar_t *fname, bool bLoadFlags )
|
||||
{
|
||||
Clear();
|
||||
@@ -937,7 +1030,7 @@ static DWORD WINAPI ThreadDownloadFile( void *param )
|
||||
params.saveRes=0;
|
||||
|
||||
std::vector<char> buf;
|
||||
params.downloadRes=DownloadFile(params.url,buf,params.fname.IsEmpty()?¶ms.fname:NULL,0,params.bAcceptCached,params.progress,params.component);
|
||||
params.downloadRes=DownloadFile(params.url,buf,params.fname.IsEmpty()?¶ms.fname:NULL,params.bAcceptCached,params.progress,params.component);
|
||||
if (params.downloadRes==DOWNLOAD_CANCEL || params.downloadRes>=DOWNLOAD_FIRST_ERROR)
|
||||
return 0;
|
||||
|
||||
@@ -971,6 +1064,7 @@ static DWORD WINAPI ThreadDownloadFile( void *param )
|
||||
return 0;
|
||||
|
||||
// validate signer
|
||||
/*
|
||||
if (params.signer)
|
||||
{
|
||||
if (params.progress)
|
||||
@@ -982,7 +1076,7 @@ static DWORD WINAPI ThreadDownloadFile( void *param )
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1089,6 +1183,12 @@ DWORD DownloadNewVersion( HWND owner, TSettingsComponent component, const wchar_
|
||||
params.bAcceptCached=true;
|
||||
params.component=component;
|
||||
|
||||
{
|
||||
const wchar_t* name = wcsrchr(url, '/');
|
||||
if (name && name[1])
|
||||
params.fname.Append(name+1);
|
||||
}
|
||||
|
||||
HANDLE hThread=CreateThread(NULL,0,ThreadDownloadFile,¶ms,0,NULL);
|
||||
|
||||
while (1)
|
||||
|
||||
@@ -31,19 +31,19 @@ struct LanguageVersionData
|
||||
|
||||
struct VersionData
|
||||
{
|
||||
bool bValid;
|
||||
DWORD newVersion;
|
||||
DWORD encodedLangVersion;
|
||||
bool bValid = false;
|
||||
DWORD newVersion = 0;
|
||||
DWORD encodedLangVersion = 0;
|
||||
CString downloadUrl;
|
||||
CString downloadSigner;
|
||||
CString news;
|
||||
CString updateLink;
|
||||
CString languageLink;
|
||||
CString altUrl;
|
||||
bool bNewVersion;
|
||||
bool bIgnoreVersion;
|
||||
bool bNewLanguage;
|
||||
bool bIgnoreLanguage;
|
||||
bool bNewVersion = false;
|
||||
bool bIgnoreVersion = false;
|
||||
bool bNewLanguage = false;
|
||||
bool bIgnoreLanguage = false;
|
||||
CString newLanguage;
|
||||
std::vector<LanguageVersionData> languages;
|
||||
|
||||
@@ -59,6 +59,8 @@ struct VersionData
|
||||
LOAD_BAD_FILE, // the file is corrupted
|
||||
};
|
||||
|
||||
TLoadResult Load();
|
||||
TLoadResult LoadNightly();
|
||||
TLoadResult Load( const wchar_t *fname, bool bLoadFlags );
|
||||
private:
|
||||
void operator=( const VersionData& );
|
||||
|
||||
@@ -83,6 +83,11 @@
|
||||
<OutDir>$(Configuration)64\</OutDir>
|
||||
<IntDir>$(Configuration)64\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>APPVEYOR_REPO_COMMIT="$(APPVEYOR_REPO_COMMIT)";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
|
||||
25447
Src/Lib/json.hpp
Normal file
25447
Src/Lib/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -40,6 +40,9 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ClassicIEDLL", "ClassicIE\ClassicIEDLL\ClassicIEDLL.vcxproj", "{BC0E6E7C-08C1-4F12-A754-4608E5A22FA8}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Update", "Update\Update.vcxproj", "{171B46B0-6083-4D9E-BD33-946EA3BD76FA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1} = {D94BD2A6-1872-4F01-B911-F406603AA2E1}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win7Aero7", "Skins\Win7Aero7\Win7Aero7.vcxproj", "{A2CCDE9F-17CE-461E-8BD9-00261B8855A6}"
|
||||
EndProject
|
||||
@@ -63,6 +66,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Metro", "Skins\Metro\Metro.
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Metallic7", "Skins\Metallic7\Metallic7.vcxproj", "{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DesktopToasts", "Update\DesktopToasts\DesktopToasts.vcxproj", "{D94BD2A6-1872-4F01-B911-F406603AA2E1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@@ -374,6 +379,15 @@ Global
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|Win32.ActiveCfg = Resource|Win32
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|Win32.Build.0 = Resource|Win32
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|x64.ActiveCfg = Resource|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|Win32.Build.0 = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|x64.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|Win32.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|Win32.Build.0 = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|x64.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -37,6 +37,8 @@ copy /B ..\ClassicIE\Setup\ClassicIEDLL_32.dll Output > nul
|
||||
copy /B ..\ClassicIE\Setup\ClassicIE_32.exe Output > nul
|
||||
copy /B ..\StartMenu\Setup\StartMenu.exe Output > nul
|
||||
copy /B ..\StartMenu\Setup\StartMenuDLL.dll Output > nul
|
||||
copy /B ..\Update\Release\Update.exe Output > nul
|
||||
copy /B ..\Update\DesktopToasts\Release\DesktopToasts.dll Output > nul
|
||||
copy /B ..\StartMenu\StartMenuHelper\Setup\StartMenuHelper32.dll Output > nul
|
||||
copy /B ..\Setup\SetupHelper\Release\SetupHelper.exe Output > nul
|
||||
|
||||
@@ -96,6 +98,10 @@ copy /B ..\StartMenu\Setup\StartMenuDLL.pdb Output\PDB32 > nul
|
||||
copy /B Output\StartMenuDLL.dll Output\PDB32 > nul
|
||||
copy /B ..\StartMenu\StartMenuHelper\Setup\StartMenuHelper32.pdb Output\PDB32 > nul
|
||||
copy /B Output\StartMenuHelper32.dll Output\PDB32 > nul
|
||||
copy /B ..\Update\Release\Update.pdb Output\PDB32 > nul
|
||||
copy /B Output\Update.exe Output\PDB32 > nul
|
||||
copy /B ..\Update\DesktopToasts\Release\DesktopToasts.pdb Output\PDB32 > nul
|
||||
copy /B Output\DesktopToasts.dll Output\PDB32 > nul
|
||||
|
||||
REM Menu 64
|
||||
copy /B ..\StartMenu\Setup64\StartMenu.pdb Output\PDB64 > nul
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
<ComponentRef Id="IESettingsLink" />
|
||||
<Condition Level="1">IE_BUILD>=90000</Condition>
|
||||
</Feature>
|
||||
<Feature Id="Update" Level="1" Title="!(loc.UpdateTitle)" ConfigurableDirectory="APPLICATIONFOLDER" AllowAdvertise="no" Description="!(loc.UpdateDesc)">
|
||||
<ComponentRef Id="Update.exe" />
|
||||
<ComponentRef Id="DesktopToasts.dll" />
|
||||
<ComponentRef Id="UpdateSettingsLink" />
|
||||
<ComponentRef Id="ToastActivator" />
|
||||
</Feature>
|
||||
</Feature>
|
||||
<UI>
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||
@@ -469,6 +475,12 @@
|
||||
<Component Id="StartMenuHelperL10N.ini" Guid="144CE4DC-0C77-4793-B79A-A32E48A90E64" Win64="$(var.CS_WIN64)">
|
||||
<File Id="StartMenuHelperL10N.ini" KeyPath="yes" Source="..\StartMenu\StartMenuHelper\StartMenuHelperL10N.ini" Vital="yes" />
|
||||
</Component>
|
||||
<Component Id="Update.exe" Guid="FB6C213F-B670-4888-8B2C-12E807E335A7" Win64="$(var.CS_WIN64)">
|
||||
<File Id="Update" KeyPath="yes" Source="Output\Update.exe" Checksum="yes" Vital="yes" />
|
||||
</Component>
|
||||
<Component Id="DesktopToasts.dll" Guid="B42157AF-3984-4796-8BD6-501FC5450FF1" Win64="$(var.CS_WIN64)">
|
||||
<File Id="DesktopToasts.dll" KeyPath="yes" Source="Output\DesktopToasts.dll" Checksum="yes" Vital="yes" />
|
||||
</Component>
|
||||
<Component Id="PolicyDefinitions.zip" Guid="580A15D0-4023-471d-9D82-9D63FBA42B5D" Win64="$(var.CS_WIN64)">
|
||||
<File Id="PolicyDefinitions.zip" KeyPath="yes" Source="Output\PolicyDefinitions.zip" Vital="yes" />
|
||||
</Component>
|
||||
@@ -544,6 +556,11 @@
|
||||
<Component Id="TreatAs" Guid="B1E7462A-E1E2-47eb-A42C-7BD272D738AA" Win64="$(var.CS_WIN64)">
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{ECD4FC4D-521C-11D0-B792-00A0C90312E1}\TreatAs" ForceDeleteOnUninstall="yes" />
|
||||
</Component>
|
||||
<Component Id="ToastActivator" Guid="3A0FDC31-D5D3-4C2E-9A0B-292CAB46DA87" Win64="$(var.CS_WIN64)">
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{E407B70A-1FBD-4D5E-8822-231C69102472}\LocalServer32" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Value=""[APPLICATIONFOLDER]Update.exe" -ToastActivated" Type="string" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="StartMenuDir">
|
||||
<Component Id="HelpLink" Guid="D631C351-7BD4-42CE-813C-5D46347068AF">
|
||||
@@ -556,6 +573,14 @@
|
||||
<CreateFolder />
|
||||
<Condition>START_MENU_FOLDER=1</Condition>
|
||||
</Component>
|
||||
<Component Id="UpdateSettingsLink" Guid="10B5A082-6C92-4EA7-AFF8-21AE3D2D7FE0">
|
||||
<Shortcut Id="UpdateSettingsLink" Name="!(loc.UpdateItem)" Advertise="no" Description="!(loc.UpdateSettingsDesc)" Target="[APPLICATIONFOLDER]Update.exe" WorkingDirectory="APPLICATIONFOLDER">
|
||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="OpenShell.Update"/>
|
||||
<ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{E407B70A-1FBD-4D5E-8822-231C69102472}"/>
|
||||
</Shortcut>
|
||||
<CreateFolder />
|
||||
<Condition>START_MENU_FOLDER=1</Condition>
|
||||
</Component>
|
||||
<Component Id="ExplorerSettingsLink" Guid="6EC027F2-115D-4110-8189-DDAFC78169EC">
|
||||
<Shortcut Id="ExplorerSettingsLink" Name="!(loc.ExplorerItem)" Advertise="no" Description="!(loc.ExplorerSettingsDesc)" Target="[APPLICATIONFOLDER]ClassicExplorerSettings.exe" WorkingDirectory="APPLICATIONFOLDER">
|
||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="OpenShell.ClassicExplorer.Settings" />
|
||||
|
||||
@@ -405,7 +405,7 @@ void ShowMetroColorViewer( void )
|
||||
if (fout) fprintf(fout,"%02X%02X%02X%02X %S\n",(color>>24)&0xFF,color&0xFF,(color>>8)&0xFF,(color>>16)&0xFF,name);
|
||||
#endif
|
||||
MetroColor mc;
|
||||
mc.name=name;
|
||||
mc.name=text;
|
||||
mc.NAME=mc.name;
|
||||
mc.NAME.MakeUpper();
|
||||
mc.type=type;
|
||||
|
||||
@@ -55,8 +55,8 @@ GUID IID_IApplicationResolver8={0xde25675a,0x72de,0x44b4,{0x93,0x73,0x05,0x17,0x
|
||||
|
||||
interface IResourceContext;
|
||||
|
||||
const GUID IID_IResourceMap={0x6e21e72b, 0xb9b0, 0x42ae, {0xa6, 0x86, 0x98, 0x3c, 0xf7, 0x84, 0xed, 0xcd}};
|
||||
interface IResourceMap : public IUnknown
|
||||
MIDL_INTERFACE("6e21e72b-b9b0-42ae-a686-983cf784edcd")
|
||||
IResourceMap : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE GetUri(const wchar_t **pUri ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetSubtree(const wchar_t *propName, IResourceMap **pSubTree ) = 0;
|
||||
@@ -76,8 +76,8 @@ enum RESOURCE_SCALE
|
||||
RES_SCALE_80 =3,
|
||||
};
|
||||
|
||||
const GUID IID_ResourceContext={0xe3c22b30, 0x8502, 0x4b2f, {0x91, 0x33, 0x55, 0x96, 0x74, 0x58, 0x7e, 0x51}};
|
||||
interface IResourceContext : public IUnknown
|
||||
MIDL_INTERFACE("e3c22b30-8502-4b2f-9133-559674587e51")
|
||||
IResourceContext : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE GetLanguage( void ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetHomeRegion( wchar_t *pRegion ) = 0;
|
||||
@@ -176,6 +176,27 @@ static void CreateMonochromeImage( unsigned int *bits, int stride, int width, in
|
||||
}
|
||||
}
|
||||
|
||||
HBITMAP ColorizeMonochromeImage(HBITMAP bitmap, DWORD color)
|
||||
{
|
||||
{
|
||||
BITMAP info{};
|
||||
GetObject(bitmap, sizeof(info), &info);
|
||||
if (!DetectGrayscaleImage((const unsigned int*)info.bmBits, info.bmWidth, info.bmWidth, info.bmHeight))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HBITMAP bmp = (HBITMAP)CopyImage(bitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
|
||||
if (bmp)
|
||||
{
|
||||
BITMAP info{};
|
||||
GetObject(bmp, sizeof(info), &info);
|
||||
|
||||
CreateMonochromeImage((unsigned int*)info.bmBits, info.bmWidth, info.bmWidth, info.bmHeight, color);
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
static HBITMAP BitmapFromMetroIcon( HICON hIcon, int bitmapSize, int iconSize, DWORD metroColor, bool bDestroyIcon=true )
|
||||
{
|
||||
ICONINFO info;
|
||||
@@ -259,7 +280,6 @@ static HBITMAP BitmapFromMetroBitmap( HBITMAP hBitmap, int bitmapSize, DWORD met
|
||||
HGDIOBJ bmp0=SelectObject(hdc,bmp);
|
||||
HGDIOBJ bmp02=SelectObject(hsrc,hBitmap);
|
||||
int offset=(bitmapSize-info.bmWidth)/2;
|
||||
bool bInvert=g_bInvertMetroIcons;
|
||||
if (g_bInvertMetroIcons && bGrayscale)
|
||||
{
|
||||
FillRect(hdc,&rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||
@@ -300,10 +320,9 @@ static HBITMAP BitmapFromMetroBitmap( HBITMAP hBitmap, int bitmapSize, DWORD met
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static HBITMAP LoadMetroBitmap0( const wchar_t *path, int bitmapSize, DWORD metroColor )
|
||||
static HBITMAP LoadMetroBitmap0(const wchar_t *path, int bitmapSize, DWORD metroColor = 0xFFFFFFFF)
|
||||
{
|
||||
int iconSize=g_bInvertMetroIcons?bitmapSize:(bitmapSize-2);
|
||||
SIZE size={-iconSize,iconSize};
|
||||
SIZE size={-bitmapSize,bitmapSize};
|
||||
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
|
||||
if (hBitmap)
|
||||
{
|
||||
@@ -440,16 +459,8 @@ static HBITMAP LoadMetroBitmap2( const wchar_t *location, int bitmapSize, DWORD
|
||||
}
|
||||
if (iconSize)
|
||||
{
|
||||
if (g_bInvertMetroIcons)
|
||||
{
|
||||
if (iconSize>bitmapSize)
|
||||
iconSize=bitmapSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iconSize>bitmapSize-2)
|
||||
iconSize=bitmapSize-2;
|
||||
}
|
||||
if (iconSize>bitmapSize)
|
||||
iconSize=bitmapSize;
|
||||
SIZE size={iconSize,iconSize};
|
||||
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
|
||||
if (hBitmap)
|
||||
@@ -1114,6 +1125,49 @@ const CItemManager::ItemInfo *CItemManager::GetCustomIcon( const wchar_t *path,
|
||||
return GetCustomIcon(text,index,iconSizeType,false);
|
||||
}
|
||||
|
||||
const CItemManager::ItemInfo* CItemManager::GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType)
|
||||
{
|
||||
wchar_t location[_MAX_PATH];
|
||||
int index;
|
||||
|
||||
if (link->GetIconLocation(location, _countof(location), &index) == S_OK && location[0])
|
||||
return GetCustomIcon(location, index, iconSizeType, (index == 0)); // assuming that if index!=0 the icon comes from a permanent location like a dll or exe
|
||||
|
||||
CComQIPtr<IPropertyStore> store(link);
|
||||
if (store)
|
||||
{
|
||||
// Name: System.AppUserModel.DestListLogoUri -- PKEY_AppUserModel_DestListLogoUri
|
||||
// Type: String -- VT_LPWSTR
|
||||
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 29
|
||||
static const PROPERTYKEY PKEY_AppUserModel_DestListLogoUri = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 29 };
|
||||
|
||||
auto logoUri = GetPropertyStoreString(store, PKEY_AppUserModel_DestListLogoUri);
|
||||
if (!logoUri.IsEmpty())
|
||||
{
|
||||
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
|
||||
if (!appId.IsEmpty())
|
||||
{
|
||||
CComPtr<IResourceManager> resManager;
|
||||
if (SUCCEEDED(resManager.CoCreateInstance(CLSID_ResourceManager)))
|
||||
{
|
||||
if (SUCCEEDED(resManager->InitializeForPackage(GetPackageFullName(appId))))
|
||||
{
|
||||
CComPtr<IResourceMap> resMap;
|
||||
if (SUCCEEDED(resManager->GetMainResourceMap(IID_PPV_ARGS(&resMap))))
|
||||
{
|
||||
CComString location;
|
||||
if (SUCCEEDED(resMap->GetFilePath(logoUri, &location)))
|
||||
return GetCustomIcon(location, -65536, iconSizeType, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CItemManager::ItemInfo *CItemManager::GetMetroAppInfo10( const wchar_t *appid )
|
||||
{
|
||||
wchar_t APPID[256];
|
||||
@@ -1824,7 +1878,11 @@ void CItemManager::RefreshItemInfo( ItemInfo *pInfo, int refreshFlags, IShellIte
|
||||
{
|
||||
newInfo.bLink=true;
|
||||
pStore=pLink;
|
||||
|
||||
#ifdef _DEBUG
|
||||
LOG_MENU(LOG_OPEN, L"Link: %s", newInfo.path);
|
||||
LOG_MENU(LOG_OPEN, L"Link property store:");
|
||||
LogPropertyStore(LOG_OPEN, pStore);
|
||||
#endif
|
||||
if (SUCCEEDED(pLink->GetIDList(&newInfo.targetPidl)))
|
||||
{
|
||||
wchar_t path[_MAX_PATH];
|
||||
@@ -1833,6 +1891,28 @@ void CItemManager::RefreshItemInfo( ItemInfo *pInfo, int refreshFlags, IShellIte
|
||||
CharUpper(path);
|
||||
newInfo.targetPATH=path;
|
||||
}
|
||||
|
||||
CComPtr<IShellItem> target;
|
||||
if (SUCCEEDED(SHCreateItemFromIDList(newInfo.targetPidl, IID_PPV_ARGS(&target))))
|
||||
{
|
||||
CComPtr<IPropertyStore> store;
|
||||
if (SUCCEEDED(target->BindToHandler(nullptr, BHID_PropertyStore, IID_PPV_ARGS(&store))))
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LOG_MENU(LOG_OPEN, L"Target property store:");
|
||||
LogPropertyStore(LOG_OPEN, store);
|
||||
#endif
|
||||
PROPVARIANT val;
|
||||
PropVariantInit(&val);
|
||||
if (SUCCEEDED(store->GetValue(PKEY_MetroAppLauncher, &val)) && (val.vt == VT_I4 || val.vt == VT_UI4) && val.intVal)
|
||||
{
|
||||
newInfo.bLink = false;
|
||||
pItem = target;
|
||||
pStore = store;
|
||||
}
|
||||
PropVariantClear(&val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1924,7 +2004,6 @@ void CItemManager::RefreshItemInfo( ItemInfo *pInfo, int refreshFlags, IShellIte
|
||||
{
|
||||
newInfo.targetPidl.Clear();
|
||||
newInfo.targetPATH.Empty();
|
||||
newInfo.metroName.Empty();
|
||||
newInfo.iconPath.Empty();
|
||||
newInfo.bNoPin=newInfo.bNoNew=false;
|
||||
if (!newInfo.bMetroApp)
|
||||
@@ -2297,12 +2376,6 @@ void CItemManager::LoadShellIcon( IShellItem *pItem, int refreshFlags, const Ico
|
||||
int smallIconSize=SMALL_ICON_SIZE;
|
||||
int largeIconSize=LARGE_ICON_SIZE;
|
||||
int extraLargeIconSize=EXTRA_LARGE_ICON_SIZE;
|
||||
if (pMetroColor)
|
||||
{
|
||||
smallIconSize-=2;
|
||||
largeIconSize-=2;
|
||||
extraLargeIconSize-=2;
|
||||
}
|
||||
HICON hSmallIcon=NULL, hLargeIcon=NULL, hExtraLargeIcon=NULL;
|
||||
if (bNotFileName)
|
||||
{
|
||||
@@ -2415,14 +2488,13 @@ void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const Ic
|
||||
if (FAILED(pResManager->InitializeForPackage(packageName)))
|
||||
return;
|
||||
CComPtr<IResourceMap> pResMap;
|
||||
if (FAILED(pResManager->GetMainResourceMap(IID_IResourceMap,(void**)&pResMap)))
|
||||
if (FAILED(pResManager->GetMainResourceMap(IID_PPV_ARGS(&pResMap))))
|
||||
return;
|
||||
CComPtr<IResourceContext> pResContext;
|
||||
if (FAILED(pResManager->GetDefaultContext(IID_ResourceContext,(void**)&pResContext)))
|
||||
if (FAILED(pResManager->GetDefaultContext(IID_PPV_ARGS(&pResContext))))
|
||||
return;
|
||||
int iconFlags=0;
|
||||
int delta=g_bInvertMetroIcons?0:2;
|
||||
if ((refreshFlags&INFO_SMALL_ICON) && SetResContextTargetSize(pResContext,SMALL_ICON_SIZE-delta))
|
||||
if ((refreshFlags&INFO_SMALL_ICON) && SetResContextTargetSize(pResContext,SMALL_ICON_SIZE))
|
||||
{
|
||||
CComString pLocation;
|
||||
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
||||
@@ -2432,7 +2504,7 @@ void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const Ic
|
||||
StoreInCache(hash,L"",hSmallBitmap,NULL,NULL,INFO_SMALL_ICON,smallIcon,largeIcon,extraLargeIcon,false,true);
|
||||
}
|
||||
}
|
||||
if ((refreshFlags&INFO_LARGE_ICON) && SetResContextTargetSize(pResContext,LARGE_ICON_SIZE-delta))
|
||||
if ((refreshFlags&INFO_LARGE_ICON) && SetResContextTargetSize(pResContext,LARGE_ICON_SIZE))
|
||||
{
|
||||
CComString pLocation;
|
||||
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
||||
@@ -2442,7 +2514,7 @@ void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const Ic
|
||||
StoreInCache(hash,L"",NULL,hLargeBitmap,NULL,INFO_LARGE_ICON,smallIcon,largeIcon,extraLargeIcon,false,true);
|
||||
}
|
||||
}
|
||||
if ((refreshFlags&INFO_EXTRA_LARGE_ICON) && SetResContextTargetSize(pResContext,EXTRA_LARGE_ICON_SIZE-delta))
|
||||
if ((refreshFlags&INFO_EXTRA_LARGE_ICON) && SetResContextTargetSize(pResContext,EXTRA_LARGE_ICON_SIZE))
|
||||
{
|
||||
CComString pLocation;
|
||||
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
||||
@@ -2587,49 +2659,45 @@ void CItemManager::IconInfo::SetPath( const wchar_t *path )
|
||||
timestamp.dwHighDateTime=timestamp.dwLowDateTime=0;
|
||||
}
|
||||
|
||||
void CItemManager::LoadCustomIcon( const wchar_t *iconPath, int iconIndex, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, bool bTemp )
|
||||
void CItemManager::LoadCustomIcon(const wchar_t *iconPath, int iconIndex, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, bool bTemp)
|
||||
{
|
||||
unsigned int hash=CalcFNVHash(iconPath,CalcFNVHash(&iconIndex,4));
|
||||
unsigned int hash = CalcFNVHash(iconPath, CalcFNVHash(&iconIndex, 4));
|
||||
|
||||
FindInCache(hash,refreshFlags,smallIcon,largeIcon,extraLargeIcon);
|
||||
if (!refreshFlags) return;
|
||||
FindInCache(hash, refreshFlags, smallIcon, largeIcon, extraLargeIcon);
|
||||
if (!refreshFlags)
|
||||
return;
|
||||
|
||||
auto ExtractIconAsBitmap = [&](int iconSize) -> HBITMAP {
|
||||
|
||||
if (iconIndex == -65536)
|
||||
return LoadMetroBitmap0(iconPath, iconSize);
|
||||
|
||||
HICON hIcon;
|
||||
|
||||
if (!*iconPath)
|
||||
hIcon = (HICON)LoadImage(g_Instance, MAKEINTRESOURCE(-iconIndex), IMAGE_ICON, iconSize, iconSize, LR_DEFAULTCOLOR);
|
||||
else
|
||||
hIcon = ShExtractIcon(iconPath, iconIndex == -1 ? 0 : iconIndex, iconSize);
|
||||
|
||||
if (hIcon)
|
||||
return BitmapFromIcon(hIcon, iconSize);
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// extract icon
|
||||
HBITMAP hSmallBitmap=NULL, hLargeBitmap=NULL, hExtraLargeBitmap=NULL;
|
||||
if (refreshFlags&INFO_SMALL_ICON)
|
||||
{
|
||||
HICON hIcon;
|
||||
if (!*iconPath)
|
||||
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,SMALL_ICON_SIZE,SMALL_ICON_SIZE,LR_DEFAULTCOLOR);
|
||||
else
|
||||
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,SMALL_ICON_SIZE);
|
||||
if (hIcon)
|
||||
hSmallBitmap=BitmapFromIcon(hIcon,SMALL_ICON_SIZE);
|
||||
}
|
||||
HBITMAP hSmallBitmap = nullptr, hLargeBitmap = nullptr, hExtraLargeBitmap = nullptr;
|
||||
|
||||
if (refreshFlags&INFO_LARGE_ICON)
|
||||
{
|
||||
HICON hIcon;
|
||||
if (!*iconPath)
|
||||
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,LARGE_ICON_SIZE,LARGE_ICON_SIZE,LR_DEFAULTCOLOR);
|
||||
else
|
||||
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,LARGE_ICON_SIZE);
|
||||
if (hIcon)
|
||||
hLargeBitmap=BitmapFromIcon(hIcon,LARGE_ICON_SIZE);
|
||||
}
|
||||
if (refreshFlags & INFO_SMALL_ICON)
|
||||
hSmallBitmap = ExtractIconAsBitmap(SMALL_ICON_SIZE);
|
||||
|
||||
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
||||
{
|
||||
HICON hIcon;
|
||||
if (!*iconPath)
|
||||
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,EXTRA_LARGE_ICON_SIZE,EXTRA_LARGE_ICON_SIZE,LR_DEFAULTCOLOR);
|
||||
else
|
||||
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,EXTRA_LARGE_ICON_SIZE);
|
||||
if (hIcon)
|
||||
hExtraLargeBitmap=BitmapFromIcon(hIcon,EXTRA_LARGE_ICON_SIZE);
|
||||
}
|
||||
if (refreshFlags & INFO_LARGE_ICON)
|
||||
hLargeBitmap = ExtractIconAsBitmap(LARGE_ICON_SIZE);
|
||||
|
||||
StoreInCache(hash,bTemp?NULL:iconPath,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,bTemp,false);
|
||||
if (refreshFlags & INFO_EXTRA_LARGE_ICON)
|
||||
hExtraLargeBitmap = ExtractIconAsBitmap(EXTRA_LARGE_ICON_SIZE);
|
||||
|
||||
StoreInCache(hash, bTemp ? nullptr : iconPath, hSmallBitmap, hLargeBitmap, hExtraLargeBitmap, refreshFlags, smallIcon, largeIcon, extraLargeIcon, bTemp, false);
|
||||
}
|
||||
|
||||
// Recursive function to preload the items for a folder
|
||||
|
||||
@@ -173,6 +173,7 @@ public:
|
||||
const ItemInfo *GetItemInfo( CString path, int refreshFlags, TLocation location=LOCATION_UNKNOWN );
|
||||
const ItemInfo *GetCustomIcon( const wchar_t *location, int index, TIconSizeType iconSizeType, bool bTemp );
|
||||
const ItemInfo *GetCustomIcon( const wchar_t *path, TIconSizeType iconSizeType );
|
||||
const ItemInfo* GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType);
|
||||
const ItemInfo *GetMetroAppInfo10( const wchar_t *appid );
|
||||
void UpdateItemInfo( const ItemInfo *pInfo, int refreshFlags, bool bHasWriteLock=false );
|
||||
void WaitForShortcuts( const POINT &balloonPos );
|
||||
@@ -466,6 +467,7 @@ bool MenuGetFileTimestamp( const wchar_t *path, FILETIME *pWriteTime, FILETIME *
|
||||
STDAPI ShGetKnownFolderPath( REFKNOWNFOLDERID rfid, PWSTR *pPath );
|
||||
STDAPI ShGetKnownFolderIDList(REFKNOWNFOLDERID rfid, PIDLIST_ABSOLUTE *pPidl );
|
||||
STDAPI ShGetKnownFolderItem(REFKNOWNFOLDERID rfid, IShellItem **ppItem );
|
||||
HBITMAP ColorizeMonochromeImage(HBITMAP bitmap, DWORD color);
|
||||
|
||||
#define TASKBAR_PINNED_ROOT L"%APPDATA%\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar"
|
||||
#define START_MENU_PINNED_ROOT L"%APPDATA%\\OpenShell\\Pinned"
|
||||
|
||||
@@ -322,6 +322,9 @@ static void AddJumpItem( CJumpGroup &group, IUnknown *pUnknown, std::vector<CCom
|
||||
}
|
||||
}
|
||||
LOG_MENU(LOG_OPEN,L"Jumplist Link Name: %s",item.name);
|
||||
#ifdef _DEBUG
|
||||
LogPropertyStore(LOG_OPEN, pStore);
|
||||
#endif
|
||||
if (!item.name.IsEmpty())
|
||||
group.items.push_back(item);
|
||||
return;
|
||||
@@ -519,91 +522,15 @@ bool GetJumplist( const wchar_t *appid, CJumpList &list, int maxCount, int maxHe
|
||||
bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &item, HWND hwnd )
|
||||
{
|
||||
Assert(GetWinVersion()>=WIN_VER_WIN7);
|
||||
if (!item.pItem) return false;
|
||||
if (!item.pItem)
|
||||
return false;
|
||||
|
||||
if (item.type==CJumpItem::TYPE_ITEM)
|
||||
{
|
||||
/* CString appid;
|
||||
{
|
||||
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
||||
appid=pAppInfo->GetAppid();
|
||||
}
|
||||
LOG_MENU(LOG_OPEN,L"Execute Item: name=%s, appid=%s",item.name,appid);*/
|
||||
CComQIPtr<IShellItem> pItem(item.pItem);
|
||||
if (!pItem)
|
||||
return false;
|
||||
/* CComString pName;
|
||||
if (FAILED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
|
||||
return false;
|
||||
wchar_t ext[_MAX_EXT];
|
||||
Strcpy(ext,_countof(ext),PathFindExtension(pName));
|
||||
|
||||
// find the correct association handler by appid and invoke it on the item
|
||||
CComPtr<IEnumAssocHandlers> pEnumHandlers;
|
||||
if (ext[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
|
||||
{
|
||||
CComPtr<IAssocHandler> pHandler;
|
||||
ULONG count;
|
||||
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
|
||||
{
|
||||
CComQIPtr<IObjectWithAppUserModelID> pObject=pHandler;
|
||||
if (pObject)
|
||||
{
|
||||
CComString pID;
|
||||
if (SUCCEEDED(pObject->GetAppID(&pID)))
|
||||
{
|
||||
// found explicit appid
|
||||
if (_wcsicmp(appid,pID)==0)
|
||||
{
|
||||
LOG_MENU(LOG_OPEN,L"Found handler appid");
|
||||
CComPtr<IDataObject> pDataObject;
|
||||
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pHandler=NULL;
|
||||
}
|
||||
pEnumHandlers=NULL;
|
||||
|
||||
// find the correct association handler by exe name and invoke it on the item
|
||||
wchar_t targetPath[_MAX_PATH];
|
||||
targetPath[0]=0;
|
||||
{
|
||||
CComPtr<IShellItem> pItem;
|
||||
SHCreateItemFromIDList(pAppInfo->GetPidl(),IID_IShellItem,(void**)&pItem);
|
||||
CComPtr<IShellLink> pLink;
|
||||
if (pItem)
|
||||
pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink);
|
||||
CAbsolutePidl target;
|
||||
if (pLink && SUCCEEDED(pLink->Resolve(NULL,SLR_INVOKE_MSI|SLR_NO_UI|SLR_NOUPDATE)) && SUCCEEDED(pLink->GetIDList(&target)))
|
||||
{
|
||||
if (FAILED(SHGetPathFromIDList(target,targetPath)))
|
||||
targetPath[0]=0;
|
||||
}
|
||||
}
|
||||
if (targetPath[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
|
||||
{
|
||||
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
|
||||
{
|
||||
CComString pExe;
|
||||
if (SUCCEEDED(pHandler->GetName(&pExe)))
|
||||
{
|
||||
if (_wcsicmp(targetPath,pExe)==0)
|
||||
{
|
||||
LOG_MENU(LOG_OPEN,L"Found handler appexe %s",targetPath);
|
||||
CComPtr<IDataObject> pDataObject;
|
||||
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pHandler=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// couldn't find a handler, execute the old way
|
||||
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE};
|
||||
execute.nShow=SW_SHOWNORMAL;
|
||||
CAbsolutePidl pidl;
|
||||
@@ -617,9 +544,50 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i
|
||||
|
||||
if (item.type==CJumpItem::TYPE_LINK)
|
||||
{
|
||||
// invoke the link through its context menu
|
||||
// Name: System.AppUserModel.HostEnvironment -- PKEY_AppUserModel_HostEnvironment
|
||||
// Type: UInt32 -- VT_UI4
|
||||
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 14
|
||||
static const PROPERTYKEY PKEY_AppUserModel_HostEnvironment = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 14 };
|
||||
|
||||
// Name: System.AppUserModel.ActivationContext -- PKEY_AppUserModel_ActivationContext
|
||||
// Type: String -- VT_LPWSTR
|
||||
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 20
|
||||
static const PROPERTYKEY PKEY_AppUserModel_ActivationContext = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 20 };
|
||||
|
||||
CComQIPtr<IContextMenu> pMenu(item.pItem);
|
||||
if (!pMenu) return false;
|
||||
CStringA params;
|
||||
|
||||
CComQIPtr<IShellLink> pLink(item.pItem);
|
||||
if (pLink)
|
||||
{
|
||||
CComQIPtr<IPropertyStore> store(pLink);
|
||||
if (store)
|
||||
{
|
||||
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
|
||||
if (!appId.IsEmpty())
|
||||
{
|
||||
CComPtr<IShellItem2> target;
|
||||
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&target))))
|
||||
{
|
||||
ULONG modern = 0;
|
||||
if (SUCCEEDED(target->GetUInt32(PKEY_AppUserModel_HostEnvironment, &modern)) && modern)
|
||||
{
|
||||
CComQIPtr<IContextMenu> targetMenu;
|
||||
if (SUCCEEDED(target->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&targetMenu))))
|
||||
{
|
||||
pMenu = targetMenu;
|
||||
params = CT2CA(GetPropertyStoreString(store, PKEY_AppUserModel_ActivationContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invoke the link through its context menu
|
||||
if (!pMenu)
|
||||
return false;
|
||||
|
||||
HRESULT hr;
|
||||
HMENU menu=CreatePopupMenu();
|
||||
hr=pMenu->QueryContextMenu(menu,0,1,1000,CMF_DEFAULTONLY);
|
||||
@@ -633,6 +601,8 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i
|
||||
{
|
||||
CMINVOKECOMMANDINFO command={sizeof(command),CMIC_MASK_FLAG_LOG_USAGE};
|
||||
command.lpVerb=MAKEINTRESOURCEA(id-1);
|
||||
if (!params.IsEmpty())
|
||||
command.lpParameters = params;
|
||||
wchar_t path[_MAX_PATH];
|
||||
GetModuleFileName(NULL,path,_countof(path));
|
||||
if (_wcsicmp(PathFindFileName(path),L"explorer.exe")==0)
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
#include "stdafx.h"
|
||||
#include "LogManager.h"
|
||||
#include "ResourceHelper.h"
|
||||
#include "ComHelper.h"
|
||||
#include <propvarutil.h>
|
||||
#include <chrono>
|
||||
|
||||
int g_LogCategories;
|
||||
static FILE *g_LogFile;
|
||||
static int g_LogTime;
|
||||
static std::chrono::time_point<std::chrono::steady_clock> g_LogTime;
|
||||
|
||||
void InitLog( int categories, const wchar_t *fname )
|
||||
{
|
||||
@@ -21,7 +24,7 @@ void InitLog( int categories, const wchar_t *fname )
|
||||
wchar_t bom=0xFEFF;
|
||||
fwrite(&bom,2,1,g_LogFile);
|
||||
g_LogCategories=categories;
|
||||
g_LogTime=GetTickCount();
|
||||
g_LogTime=std::chrono::steady_clock::now();
|
||||
LogMessage(L"version=%x, PID=%d, TID=%d, Categories=%08x\r\n",GetWinVersion(),GetCurrentProcessId(),GetCurrentThreadId(),categories);
|
||||
}
|
||||
}
|
||||
@@ -38,7 +41,7 @@ void LogMessage( const wchar_t *text, ... )
|
||||
if (!g_LogFile) return;
|
||||
|
||||
wchar_t buf[2048];
|
||||
int len=Sprintf(buf,_countof(buf),L"%8d: ",GetTickCount()-g_LogTime);
|
||||
int len=Sprintf(buf,_countof(buf),L"%8d: ",std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-g_LogTime).count());
|
||||
fwrite(buf,2,len,g_LogFile);
|
||||
|
||||
va_list args;
|
||||
@@ -51,3 +54,31 @@ void LogMessage( const wchar_t *text, ... )
|
||||
|
||||
fflush(g_LogFile);
|
||||
}
|
||||
|
||||
void LogPropertyStore(TLogCategory category, IPropertyStore* store)
|
||||
{
|
||||
if (!store)
|
||||
return;
|
||||
|
||||
DWORD count = 0;
|
||||
store->GetCount(&count);
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
PROPERTYKEY key{};
|
||||
store->GetAt(i, &key);
|
||||
|
||||
PROPVARIANT val;
|
||||
PropVariantInit(&val);
|
||||
|
||||
store->GetValue(key, &val);
|
||||
|
||||
CComString valueStr;
|
||||
PropVariantToStringAlloc(val, &valueStr);
|
||||
PropVariantClear(&val);
|
||||
|
||||
wchar_t guidStr[100]{};
|
||||
StringFromGUID2(key.fmtid, guidStr, _countof(guidStr));
|
||||
|
||||
LOG_MENU(category, L"Property: {%s, %u} = %s", guidStr, key.pid, valueStr ? valueStr : L"???");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <propsys.h>
|
||||
|
||||
// LogManager.h - logging functionality (for debugging)
|
||||
// Logs different events in the start menu
|
||||
// Turn it on by setting the LogLevel setting in the registry
|
||||
@@ -33,3 +35,5 @@ void CloseLog( void );
|
||||
void LogMessage( const wchar_t *text, ... );
|
||||
|
||||
#define STARTUP_LOG L"Software\\OpenShell\\StartMenu\\Settings|LogStartup|%LOCALAPPDATA%\\OpenShell\\StartupLog.txt"
|
||||
|
||||
void LogPropertyStore(TLogCategory category, IPropertyStore* store);
|
||||
|
||||
@@ -1937,15 +1937,6 @@ void CMenuContainer::GetRecentPrograms( std::vector<MenuItem> &items, int maxCou
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
CComPtr<IShellItem> pAppItem;
|
||||
if (FAILED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,uaItem.name,IID_IShellItem,(void**)&pAppItem)))
|
||||
continue;
|
||||
CComString pName;
|
||||
if (FAILED(pAppItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)) || wcsncmp(pName,L"@{",2)==0)
|
||||
continue;
|
||||
}
|
||||
|
||||
uaItem.pLinkInfo=g_ItemManager.GetMetroAppInfo10(uaItem.name);
|
||||
if (!uaItem.pLinkInfo)
|
||||
{
|
||||
@@ -1956,6 +1947,11 @@ void CMenuContainer::GetRecentPrograms( std::vector<MenuItem> &items, int maxCou
|
||||
LOG_MENU(LOG_MFU,L"UserAssist: '%s', %d, %.3f",uaItem.name,data.count,uaItem.rank);
|
||||
{
|
||||
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
||||
if (uaItem.pLinkInfo->GetMetroName().IsEmpty() || wcsncmp(uaItem.pLinkInfo->GetMetroName(), L"@{",2)==0)
|
||||
{
|
||||
LOG_MENU(LOG_MFU, L"UserAssist: Dropping: No metro name");
|
||||
continue;
|
||||
}
|
||||
if (uaItem.pLinkInfo->IsNoPin())
|
||||
{
|
||||
LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No pin");
|
||||
@@ -2207,10 +2203,7 @@ void CMenuContainer::AddJumpListItems( std::vector<MenuItem> &items )
|
||||
if (pLink)
|
||||
{
|
||||
pLink->GetIDList(&item.pItem1);
|
||||
wchar_t location[_MAX_PATH];
|
||||
int index;
|
||||
if (pLink->GetIconLocation(location,_countof(location),&index)==S_OK && location[0])
|
||||
item.pItemInfo=g_ItemManager.GetCustomIcon(location,index,CItemManager::ICON_SIZE_TYPE_SMALL,(index==0)); // assuming that if index!=0 the icon comes from a permanent location like a dll or exe
|
||||
item.pItemInfo = g_ItemManager.GetLinkIcon(pLink, CItemManager::ICON_SIZE_TYPE_SMALL);
|
||||
}
|
||||
}
|
||||
else if (jumpItem.type==CJumpItem::TYPE_ITEM)
|
||||
@@ -6672,8 +6665,7 @@ bool CMenuContainer::GetDescription( int index, wchar_t *text, int size )
|
||||
{
|
||||
if (SUCCEEDED(pLink->GetDescription(text,size)) && text[0])
|
||||
return true;
|
||||
wchar_t args[256];
|
||||
if (SUCCEEDED(pLink->GetArguments(args,_countof(args))) && args[0])
|
||||
if (jumpItem.bHasArguments)
|
||||
{
|
||||
// don't use default tip for items with arguments
|
||||
Strcpy(text,size,item.name);
|
||||
@@ -7331,8 +7323,9 @@ static void NewVersionCallback( VersionData &data )
|
||||
wchar_t cmdLine[1024];
|
||||
Sprintf(cmdLine,_countof(cmdLine),L"\"%s\" -popup",path);
|
||||
STARTUPINFO startupInfo={sizeof(startupInfo)};
|
||||
PROCESS_INFORMATION processInfo;
|
||||
memset(&processInfo,0,sizeof(processInfo));
|
||||
// don't display busy cursor as we are doing this on background
|
||||
startupInfo.dwFlags=STARTF_FORCEOFFFEEDBACK;
|
||||
PROCESS_INFORMATION processInfo{};
|
||||
if (CreateProcess(path,cmdLine,NULL,NULL,TRUE,0,NULL,NULL,&startupInfo,&processInfo))
|
||||
{
|
||||
CloseHandle(processInfo.hThread);
|
||||
|
||||
@@ -2200,6 +2200,21 @@ void CMenuContainer::DrawBackground( HDC hdc, const RECT &drawRect )
|
||||
else
|
||||
iconSize.cx=iconSize.cy=0;
|
||||
|
||||
COLORREF color, shadowColor;
|
||||
{
|
||||
bool bHotColor = (bHot && !bSplit) || stateLeft > 0;
|
||||
if (item.id == MENU_EMPTY || item.id == MENU_EMPTY_TOP)
|
||||
{
|
||||
color = settings.textColors[bHotColor ? 3 : 2];
|
||||
shadowColor = settings.textShadowColors[bHotColor ? 3 : 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
color = settings.textColors[bHotColor ? 1 : 0];
|
||||
shadowColor = settings.textShadowColors[bHotColor ? 1 : 0];
|
||||
}
|
||||
}
|
||||
|
||||
// draw icon
|
||||
if (drawType==MenuSkin::PROGRAMS_BUTTON || drawType==MenuSkin::PROGRAMS_BUTTON_NEW)
|
||||
{
|
||||
@@ -2256,15 +2271,21 @@ void CMenuContainer::DrawBackground( HDC hdc, const RECT &drawRect )
|
||||
const CItemManager::IconInfo *pIcon=(settings.iconSize==MenuSkin::ICON_SIZE_LARGE)?item.pItemInfo->largeIcon:item.pItemInfo->smallIcon;
|
||||
if (pIcon && pIcon->bitmap)
|
||||
{
|
||||
HBITMAP temp = ColorizeMonochromeImage(pIcon->bitmap, color);
|
||||
HBITMAP bitmap = temp ? temp : pIcon->bitmap;
|
||||
|
||||
BITMAP info;
|
||||
GetObject(pIcon->bitmap,sizeof(info),&info);
|
||||
HGDIOBJ bmp0=SelectObject(hdc2,pIcon->bitmap);
|
||||
GetObject(bitmap,sizeof(info),&info);
|
||||
HGDIOBJ bmp0=SelectObject(hdc2,bitmap);
|
||||
if (bmp0)
|
||||
{
|
||||
BLENDFUNCTION func={AC_SRC_OVER,0,255,AC_SRC_ALPHA};
|
||||
AlphaBlend(hdc,iconX,iconY,iconSize.cx,iconSize.cy,hdc2,0,0,info.bmWidth,info.bmHeight,func);
|
||||
SelectObject(hdc2,bmp0);
|
||||
}
|
||||
|
||||
if (temp)
|
||||
DeleteObject(temp);
|
||||
}
|
||||
}
|
||||
else if (item.id==MENU_SHUTDOWN_BUTTON && s_bHasUpdates && s_Skin.Shutdown_bitmap.GetBitmap())
|
||||
@@ -2287,18 +2308,6 @@ void CMenuContainer::DrawBackground( HDC hdc, const RECT &drawRect )
|
||||
|
||||
// draw text
|
||||
SelectObject(hdc,settings.font);
|
||||
COLORREF color, shadowColor;
|
||||
bool bHotColor=(bHot && !bSplit) || stateLeft>0;
|
||||
if (item.id==MENU_EMPTY || item.id==MENU_EMPTY_TOP)
|
||||
{
|
||||
color=settings.textColors[bHotColor?3:2];
|
||||
shadowColor=settings.textShadowColors[bHotColor?3:2];
|
||||
}
|
||||
else
|
||||
{
|
||||
color=settings.textColors[bHotColor?1:0];
|
||||
shadowColor=settings.textShadowColors[bHotColor?1:0];
|
||||
}
|
||||
RECT rc={itemRect.left+settings.iconPadding.left+settings.iconPadding.right+settings.textPadding.left,itemRect.top+settings.textPadding.top,
|
||||
itemRect.right-settings.arrPadding.cx-settings.arrPadding.cy-settings.textPadding.right,itemRect.bottom-settings.textPadding.bottom};
|
||||
if (item.id==MENU_SHUTDOWN_BUTTON)
|
||||
|
||||
@@ -301,21 +301,12 @@ bool CanUninstallMetroApp( const wchar_t *appid )
|
||||
// Uninstalls the app with the given id
|
||||
void UninstallMetroApp( const wchar_t *appid )
|
||||
{
|
||||
CComPtr<IShellItem> pAppItem;
|
||||
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem,(void**)&pAppItem)))
|
||||
auto packageName = GetPackageFullName(appid);
|
||||
if (!packageName.IsEmpty())
|
||||
{
|
||||
CComPtr<IPropertyStore> pStore;
|
||||
pAppItem->BindToHandler(NULL,BHID_PropertyStore,IID_IPropertyStore,(void**)&pStore);
|
||||
if (pStore)
|
||||
{
|
||||
CString packageName=GetPropertyStoreString(pStore,PKEY_MetroPackageName);
|
||||
if (!packageName.IsEmpty())
|
||||
{
|
||||
wchar_t command[1024];
|
||||
Sprintf(command,_countof(command),L"Remove-AppxPackage %s",packageName);
|
||||
ShellExecute(NULL,L"open",L"powershell.exe",command,NULL,SW_HIDE);
|
||||
}
|
||||
}
|
||||
wchar_t command[1024];
|
||||
Sprintf(command, _countof(command), L"Remove-AppxPackage %s", packageName);
|
||||
ShellExecute(NULL, L"open", L"powershell.exe", command, NULL, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,3 +372,16 @@ bool IsEdgeDefaultBrowser( void )
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CString GetPackageFullName(const wchar_t* appId)
|
||||
{
|
||||
CComPtr<IShellItem> item;
|
||||
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&item))))
|
||||
{
|
||||
CComPtr<IPropertyStore> store;
|
||||
if (SUCCEEDED(item->BindToHandler(nullptr, BHID_PropertyStore, IID_PPV_ARGS(&store))))
|
||||
return GetPropertyStoreString(store, PKEY_MetroPackageName);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -53,3 +53,6 @@ CComPtr<IContextMenu> GetMetroPinMenu( const wchar_t *appid );
|
||||
|
||||
// Determines if Edge is the default browser
|
||||
bool IsEdgeDefaultBrowser( void );
|
||||
|
||||
// Returns full package name for given App ID
|
||||
CString GetPackageFullName(const wchar_t* appId);
|
||||
|
||||
@@ -375,7 +375,10 @@ COLORREF MenuSkin::GetMetroColor( const wchar_t *names ) const
|
||||
if (GetImmersiveUserColorSetPreference!=NULL)
|
||||
{
|
||||
wchar_t text[256];
|
||||
Sprintf(text,_countof(text),L"Immersive%s",name);
|
||||
if (wcsncmp(name,L"Immersive",9)==0)
|
||||
wcscpy_s(text,name);
|
||||
else
|
||||
Sprintf(text,_countof(text),L"Immersive%s",name);
|
||||
int type=GetImmersiveColorTypeFromName(text);
|
||||
data.colorType=type<0?-1:type;
|
||||
if (type>=0)
|
||||
@@ -1495,8 +1498,21 @@ bool MenuSkin::ComputeOptionStates( const std::map<CString,CString> &options, st
|
||||
values.push_back(L"ALL_PROGRAMS");
|
||||
if (SkinType==SKIN_TYPE_CLASSIC2)
|
||||
values.push_back(L"TWO_COLUMNS");
|
||||
// for compatibility with existing skins
|
||||
if (Dpi>=144)
|
||||
values.push_back(L"HIGH_DPI");
|
||||
if (Dpi>=240)
|
||||
values.push_back(L"240_DPI"); // 250% scaling
|
||||
else if (Dpi>=216)
|
||||
values.push_back(L"216_DPI"); // 225% scaling
|
||||
else if (Dpi>=192)
|
||||
values.push_back(L"192_DPI"); // 200% scaling
|
||||
else if (Dpi>=168)
|
||||
values.push_back(L"168_DPI"); // 175% scaling
|
||||
else if (Dpi>=144)
|
||||
values.push_back(L"144_DPI"); // 150% scaling
|
||||
else if (Dpi>=120)
|
||||
values.push_back(L"120_DPI"); // 125% scaling
|
||||
if (ForceTouch || (GetWinVersion()>=WIN_VER_WIN8 && GetSettingBool(L"EnableTouch") && (GetSystemMetrics(SM_DIGITIZER)&NID_INTEGRATED_TOUCH)!=0))
|
||||
values.push_back(L"TOUCH_ENABLED");
|
||||
if (GetSettingInt(L"SearchBox")!=SEARCHBOX_HIDDEN)
|
||||
|
||||
@@ -19,7 +19,6 @@ struct ICIVERBTOIDMAP
|
||||
static const ICIVERBTOIDMAP g_ContextMenuIDMap[] =
|
||||
{
|
||||
{ L"open", "open", MENUVERB_OPEN },
|
||||
{ NULL, NULL, (UINT)-1 }
|
||||
};
|
||||
|
||||
HRESULT _MapICIVerbToCmdID(LPCMINVOKECOMMANDINFO pici, UINT* pid)
|
||||
|
||||
291
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.cpp
Normal file
291
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
#include "DesktopNotificationManagerCompat.h"
|
||||
#include <appmodel.h>
|
||||
#include <wrl\wrappers\corewrappers.h>
|
||||
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT _hrTemp = hr; if (FAILED(_hrTemp)) { return _hrTemp; } } while (false)
|
||||
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
namespace DesktopNotificationManagerCompat
|
||||
{
|
||||
HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[]);
|
||||
HRESULT EnsureRegistered();
|
||||
bool IsRunningAsUwp();
|
||||
|
||||
bool s_registeredAumidAndComServer = false;
|
||||
std::wstring s_aumid;
|
||||
bool s_registeredActivator = false;
|
||||
bool s_hasCheckedIsRunningAsUwp = false;
|
||||
bool s_isRunningAsUwp = false;
|
||||
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid)
|
||||
{
|
||||
/*
|
||||
// If running as Desktop Bridge
|
||||
if (IsRunningAsUwp())
|
||||
{
|
||||
// Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
|
||||
// Desktop Bridge apps are registered with platform through their manifest.
|
||||
// Their LocalServer32 key is also registered through their manifest.
|
||||
s_aumid = L"";
|
||||
s_registeredAumidAndComServer = true;
|
||||
return S_OK;
|
||||
}
|
||||
*/
|
||||
// Copy the aumid
|
||||
s_aumid = std::wstring(aumid);
|
||||
/*
|
||||
// Get the EXE path
|
||||
wchar_t exePath[MAX_PATH];
|
||||
DWORD charWritten = ::GetModuleFileName(nullptr, exePath, ARRAYSIZE(exePath));
|
||||
RETURN_IF_FAILED(charWritten > 0 ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||
|
||||
// Register the COM server
|
||||
RETURN_IF_FAILED(RegisterComServer(clsid, exePath));
|
||||
*/
|
||||
s_registeredAumidAndComServer = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegisterActivator()
|
||||
{
|
||||
// Module<OutOfProc> needs a callback registered before it can be used.
|
||||
// Since we don't care about when it shuts down, we'll pass an empty lambda here.
|
||||
Module<OutOfProc>::Create([] {});
|
||||
|
||||
// If a local server process only hosts the COM object then COM expects
|
||||
// the COM server host to shutdown when the references drop to zero.
|
||||
// Since the user might still be using the program after activating the notification,
|
||||
// we don't want to shutdown immediately. Incrementing the object count tells COM that
|
||||
// we aren't done yet.
|
||||
Module<OutOfProc>::GetModule().IncrementObjectCount();
|
||||
|
||||
RETURN_IF_FAILED(Module<OutOfProc>::GetModule().RegisterObjects());
|
||||
|
||||
s_registeredActivator = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[])
|
||||
{
|
||||
// Turn the GUID into a string
|
||||
OLECHAR* clsidOlechar;
|
||||
StringFromCLSID(clsid, &clsidOlechar);
|
||||
std::wstring clsidStr(clsidOlechar);
|
||||
::CoTaskMemFree(clsidOlechar);
|
||||
|
||||
// Create the subkey
|
||||
// Something like SOFTWARE\Classes\CLSID\{23A5B06E-20BB-4E7E-A0AC-6982ED6A6041}\LocalServer32
|
||||
std::wstring subKey = LR"(SOFTWARE\Classes\CLSID\)" + clsidStr + LR"(\LocalServer32)";
|
||||
|
||||
// Include -ToastActivated launch args on the exe
|
||||
std::wstring exePathStr(exePath);
|
||||
exePathStr = L"\"" + exePathStr + L"\" " + TOAST_ACTIVATED_LAUNCH_ARG;
|
||||
|
||||
// We don't need to worry about overflow here as ::GetModuleFileName won't
|
||||
// return anything bigger than the max file system path (much fewer than max of DWORD).
|
||||
DWORD dataSize = static_cast<DWORD>((exePathStr.length() + 1) * sizeof(WCHAR));
|
||||
|
||||
// Register the EXE for the COM server
|
||||
return HRESULT_FROM_WIN32(::RegSetKeyValue(
|
||||
HKEY_CURRENT_USER,
|
||||
subKey.c_str(),
|
||||
nullptr,
|
||||
REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(exePathStr.c_str()),
|
||||
dataSize));
|
||||
}
|
||||
|
||||
HRESULT CreateToastNotifier(IToastNotifier **notifier)
|
||||
{
|
||||
RETURN_IF_FAILED(EnsureRegistered());
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics> toastStatics;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(),
|
||||
&toastStatics));
|
||||
|
||||
if (s_aumid.empty())
|
||||
{
|
||||
return toastStatics->CreateToastNotifier(notifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toastStatics->CreateToastNotifierWithId(HStringReference(s_aumid.c_str()).Get(), notifier);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CreateXmlDocumentFromString(const wchar_t *xmlString, IXmlDocument **doc)
|
||||
{
|
||||
ComPtr<IXmlDocument> answer;
|
||||
RETURN_IF_FAILED(Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(), &answer));
|
||||
|
||||
ComPtr<IXmlDocumentIO> docIO;
|
||||
RETURN_IF_FAILED(answer.As(&docIO));
|
||||
|
||||
// Load the XML string
|
||||
RETURN_IF_FAILED(docIO->LoadXml(HStringReference(xmlString).Get()));
|
||||
|
||||
return answer.CopyTo(doc);
|
||||
}
|
||||
|
||||
HRESULT CreateToastNotification(IXmlDocument *content, IToastNotification **notification)
|
||||
{
|
||||
ComPtr<IToastNotificationFactory> factory;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(),
|
||||
&factory));
|
||||
|
||||
return factory->CreateToastNotification(content, notification);
|
||||
}
|
||||
|
||||
HRESULT get_History(std::unique_ptr<DesktopNotificationHistoryCompat>* history)
|
||||
{
|
||||
RETURN_IF_FAILED(EnsureRegistered());
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics> toastStatics;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(),
|
||||
&toastStatics));
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics2> toastStatics2;
|
||||
RETURN_IF_FAILED(toastStatics.As(&toastStatics2));
|
||||
|
||||
ComPtr<IToastNotificationHistory> nativeHistory;
|
||||
RETURN_IF_FAILED(toastStatics2->get_History(&nativeHistory));
|
||||
|
||||
*history = std::unique_ptr<DesktopNotificationHistoryCompat>(new DesktopNotificationHistoryCompat(s_aumid.c_str(), nativeHistory));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool CanUseHttpImages()
|
||||
{
|
||||
return IsRunningAsUwp();
|
||||
}
|
||||
|
||||
HRESULT EnsureRegistered()
|
||||
{
|
||||
// If not registered AUMID yet
|
||||
if (!s_registeredAumidAndComServer)
|
||||
{
|
||||
// Check if Desktop Bridge
|
||||
if (IsRunningAsUwp())
|
||||
{
|
||||
// Implicitly registered, all good!
|
||||
s_registeredAumidAndComServer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, incorrect usage, must call RegisterAumidAndComServer first
|
||||
return E_ILLEGAL_METHOD_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
// If not registered activator yet
|
||||
if (!s_registeredActivator)
|
||||
{
|
||||
// Incorrect usage, must call RegisterActivator first
|
||||
return E_ILLEGAL_METHOD_CALL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool IsRunningAsUwp()
|
||||
{
|
||||
if (!s_hasCheckedIsRunningAsUwp)
|
||||
{
|
||||
// https://stackoverflow.com/questions/39609643/determine-if-c-application-is-running-as-a-uwp-app-in-desktop-bridge-project
|
||||
UINT32 length;
|
||||
wchar_t packageFamilyName[PACKAGE_FAMILY_NAME_MAX_LENGTH + 1];
|
||||
LONG result = GetPackageFamilyName(GetCurrentProcess(), &length, packageFamilyName);
|
||||
s_isRunningAsUwp = result == ERROR_SUCCESS;
|
||||
s_hasCheckedIsRunningAsUwp = true;
|
||||
}
|
||||
|
||||
return s_isRunningAsUwp;
|
||||
}
|
||||
}
|
||||
|
||||
DesktopNotificationHistoryCompat::DesktopNotificationHistoryCompat(const wchar_t *aumid, ComPtr<IToastNotificationHistory> history)
|
||||
{
|
||||
m_aumid = std::wstring(aumid);
|
||||
m_history = history;
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::Clear()
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->ClearWithId(HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::GetHistory(ABI::Windows::Foundation::Collections::IVectorView<ToastNotification*> **toasts)
|
||||
{
|
||||
ComPtr<IToastNotificationHistory2> history2;
|
||||
RETURN_IF_FAILED(m_history.As(&history2));
|
||||
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return history2->GetHistory(toasts);
|
||||
}
|
||||
else
|
||||
{
|
||||
return history2->GetHistoryWithId(HStringReference(m_aumid.c_str()).Get(), toasts);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::Remove(const wchar_t *tag)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->Remove(HStringReference(tag).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupedTagWithId(HStringReference(tag).Get(), HStringReference(L"").Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::RemoveGroupedTag(const wchar_t *tag, const wchar_t *group)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->RemoveGroupedTag(HStringReference(tag).Get(), HStringReference(group).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupedTagWithId(HStringReference(tag).Get(), HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::RemoveGroup(const wchar_t *group)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->RemoveGroup(HStringReference(group).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupWithId(HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
108
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.h
Normal file
108
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <Windows.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <wrl.h>
|
||||
#define TOAST_ACTIVATED_LAUNCH_ARG L"-ToastActivated"
|
||||
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
|
||||
class DesktopNotificationHistoryCompat;
|
||||
|
||||
namespace DesktopNotificationManagerCompat
|
||||
{
|
||||
/// <summary>
|
||||
/// If not running under the Desktop Bridge, you must call this method to register your AUMID with the Compat library and to
|
||||
/// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running
|
||||
/// under Desktop Bridge. Call this upon application startup, before calling any other APIs.
|
||||
/// </summary>
|
||||
/// <param name="aumid">An AUMID that uniquely identifies your application.</param>
|
||||
/// <param name="clsid">The CLSID of your NotificationActivator class.</param>
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid);
|
||||
|
||||
/// <summary>
|
||||
/// Registers your module to handle COM activations. Call this upon application startup.
|
||||
/// </summary>
|
||||
HRESULT RegisterActivator();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notifier. You must have called RegisterActivator first (and also RegisterAumidAndComServer if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
HRESULT CreateToastNotifier(IToastNotifier** notifier);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XmlDocument initialized with the specified string. This is simply a convenience helper method.
|
||||
/// </summary>
|
||||
HRESULT CreateXmlDocumentFromString(const wchar_t *xmlString, ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notification. This is simply a convenience helper method.
|
||||
/// </summary>
|
||||
HRESULT CreateToastNotification(ABI::Windows::Data::Xml::Dom::IXmlDocument* content, IToastNotification** notification);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DesktopNotificationHistoryCompat object. You must have called RegisterActivator first (and also RegisterAumidAndComServer if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
HRESULT get_History(std::unique_ptr<DesktopNotificationHistoryCompat>* history);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge.
|
||||
/// </summary>
|
||||
bool CanUseHttpImages();
|
||||
}
|
||||
|
||||
class DesktopNotificationHistoryCompat
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Removes all notifications sent by this app from action center.
|
||||
/// </summary>
|
||||
HRESULT Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all notifications sent by this app that are currently still in Action Center.
|
||||
/// </summary>
|
||||
HRESULT GetHistory(ABI::Windows::Foundation::Collections::IVectorView<ToastNotification*>** history);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an individual toast, with the specified tag label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
HRESULT Remove(const wchar_t *tag);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a toast notification from the action using the notification's tag and group labels.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
/// <param name="group">The group label of the toast notification to be removed.</param>
|
||||
HRESULT RemoveGroupedTag(const wchar_t *tag, const wchar_t *group);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a group of toast notifications, identified by the specified group label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="group">The group label of the toast notifications to be removed.</param>
|
||||
HRESULT RemoveGroup(const wchar_t *group);
|
||||
|
||||
/// <summary>
|
||||
/// Do not call this. Instead, call DesktopNotificationManagerCompat.get_History() to obtain an instance.
|
||||
/// </summary>
|
||||
DesktopNotificationHistoryCompat(const wchar_t *aumid, Microsoft::WRL::ComPtr<IToastNotificationHistory> history);
|
||||
|
||||
private:
|
||||
std::wstring m_aumid;
|
||||
Microsoft::WRL::ComPtr<IToastNotificationHistory> m_history = nullptr;
|
||||
};
|
||||
5
Src/Update/DesktopToasts/DesktopToasts.def
Normal file
5
Src/Update/DesktopToasts/DesktopToasts.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY "DesktopToasts.dll"
|
||||
|
||||
EXPORTS
|
||||
Initialize
|
||||
DisplaySimpleToast
|
||||
59
Src/Update/DesktopToasts/DesktopToasts.h
Normal file
59
Src/Update/DesktopToasts/DesktopToasts.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
using DesktopToastActivateHandler = void(__cdecl*)(void* context, LPCWSTR invokedArgs);
|
||||
|
||||
HRESULT Initialize(LPCWSTR appUserModelId, DesktopToastActivateHandler handler, void* handlerContext);
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text);
|
||||
|
||||
class DesktopToasts
|
||||
{
|
||||
public:
|
||||
explicit DesktopToasts(LPCWSTR appUserModelId)
|
||||
{
|
||||
if (::IsWindows10OrGreater())
|
||||
{
|
||||
auto m_lib = ::LoadLibrary(L"DesktopToasts.dll");
|
||||
if (m_lib)
|
||||
{
|
||||
m_pInitialize = (decltype(m_pInitialize))::GetProcAddress(m_lib, "Initialize");
|
||||
m_pDisplayToast = (decltype(m_pDisplayToast))::GetProcAddress(m_lib, "DisplaySimpleToast");
|
||||
|
||||
if (m_pInitialize && m_pDisplayToast)
|
||||
{
|
||||
if (m_pInitialize(appUserModelId, ToastActivate, this) == S_OK)
|
||||
m_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DesktopToasts()
|
||||
{
|
||||
if (m_lib)
|
||||
::FreeLibrary(m_lib);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text)
|
||||
{
|
||||
return m_pDisplayToast(title, text);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void OnToastActivate(LPCWSTR invokedArgs) {}
|
||||
|
||||
static void __cdecl ToastActivate(void* context, LPCWSTR invokedArgs)
|
||||
{
|
||||
static_cast<DesktopToasts*>(context)->OnToastActivate(invokedArgs);
|
||||
}
|
||||
|
||||
bool m_initialized = false;
|
||||
|
||||
HMODULE m_lib = nullptr;
|
||||
decltype(&::Initialize) m_pInitialize = nullptr;
|
||||
decltype(&::DisplaySimpleToast) m_pDisplayToast = nullptr;
|
||||
};
|
||||
61
Src/Update/DesktopToasts/DesktopToasts.rc
Normal file
61
Src/Update/DesktopToasts/DesktopToasts.rc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION _PRODUCT_VERSION
|
||||
PRODUCTVERSION _PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Open-Shell"
|
||||
VALUE "FileDescription", "Desktop toast notifications support"
|
||||
VALUE "FileVersion", _PRODUCT_VERSION_STR
|
||||
VALUE "InternalName", "DesktopToasts.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2020, The Open-Shell Team"
|
||||
VALUE "OriginalFilename", "DesktopToasts.dll"
|
||||
VALUE "ProductName", "Open-Shell"
|
||||
VALUE "ProductVersion", _PRODUCT_VERSION_STR
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
110
Src/Update/DesktopToasts/DesktopToasts.vcxproj
Normal file
110
Src/Update/DesktopToasts/DesktopToasts.vcxproj
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{d94bd2a6-1872-4f01-b911-f406603aa2e1}</ProjectGuid>
|
||||
<RootNamespace>DesktopToasts</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<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" />
|
||||
<Import Project="..\..\Version.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\Version.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>..\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;DESKTOPTOASTS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>DesktopToasts.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;DESKTOPTOASTS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>DesktopToasts.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DesktopNotificationManagerCompat.h" />
|
||||
<ClInclude Include="DesktopToasts.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DesktopNotificationManagerCompat.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DesktopToasts.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="DesktopToasts.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
43
Src/Update/DesktopToasts/DesktopToasts.vcxproj.filters
Normal file
43
Src/Update/DesktopToasts/DesktopToasts.vcxproj.filters
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DesktopNotificationManagerCompat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DesktopToasts.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DesktopNotificationManagerCompat.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DesktopToasts.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="DesktopToasts.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
131
Src/Update/DesktopToasts/dllmain.cpp
Normal file
131
Src/Update/DesktopToasts/dllmain.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#include <windows.h>
|
||||
#include <NotificationActivationCallback.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
|
||||
#include "DesktopToasts.h"
|
||||
#include "DesktopNotificationManagerCompat.h"
|
||||
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT _hrTemp = hr; if (FAILED(_hrTemp)) { return _hrTemp; } } while (false)
|
||||
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
DesktopToastActivateHandler g_handler = nullptr;
|
||||
void* g_handlerContext = nullptr;
|
||||
|
||||
class DECLSPEC_UUID("E407B70A-1FBD-4D5E-8822-231C69102472") 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
|
||||
{
|
||||
if (g_handler)
|
||||
g_handler(g_handlerContext, invokedArgs);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// Flag class as COM creatable
|
||||
CoCreatableClass(NotificationActivator);
|
||||
|
||||
HRESULT Initialize(LPCWSTR appUserModelId, DesktopToastActivateHandler handler, void* handlerContext)
|
||||
{
|
||||
RETURN_IF_FAILED(DesktopNotificationManagerCompat::RegisterAumidAndComServer(appUserModelId, __uuidof(NotificationActivator)));
|
||||
RETURN_IF_FAILED(DesktopNotificationManagerCompat::RegisterActivator());
|
||||
g_handler = handler;
|
||||
g_handlerContext = handlerContext;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT SetNodeValueString(HSTRING inputString, IXmlNode* node, IXmlDocument* xml)
|
||||
{
|
||||
ComPtr<IXmlText> inputText;
|
||||
RETURN_IF_FAILED(xml->CreateTextNode(inputString, &inputText));
|
||||
|
||||
ComPtr<IXmlNode> inputTextNode;
|
||||
RETURN_IF_FAILED(inputText.As(&inputTextNode));
|
||||
|
||||
ComPtr<IXmlNode> appendedChild;
|
||||
return node->AppendChild(inputTextNode.Get(), &appendedChild);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
HRESULT SetTextValues(const PCWSTR* textValues, UINT32 textValuesCount, IXmlDocument* toastXml)
|
||||
{
|
||||
ComPtr<IXmlNodeList> nodeList;
|
||||
RETURN_IF_FAILED(toastXml->GetElementsByTagName(HStringReference(L"text").Get(), &nodeList));
|
||||
|
||||
UINT32 nodeListLength;
|
||||
RETURN_IF_FAILED(nodeList->get_Length(&nodeListLength));
|
||||
|
||||
// If a template was chosen with fewer text elements, also change the amount of strings
|
||||
// passed to this method.
|
||||
RETURN_IF_FAILED(textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG);
|
||||
|
||||
for (UINT32 i = 0; i < textValuesCount; i++)
|
||||
{
|
||||
ComPtr<IXmlNode> textNode;
|
||||
RETURN_IF_FAILED(nodeList->Item(i, &textNode));
|
||||
|
||||
RETURN_IF_FAILED(SetNodeValueString(HStringReference(textValues[i]).Get(), textNode.Get(), toastXml));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text)
|
||||
{
|
||||
// Construct XML
|
||||
ComPtr<IXmlDocument> doc;
|
||||
HRESULT hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(L"<toast><visual><binding template='ToastGeneric'><text></text><text></text></binding></visual></toast>", &doc);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
PCWSTR textValues[] = { title, text };
|
||||
SetTextValues(textValues, ARRAYSIZE(textValues), doc.Get());
|
||||
|
||||
// Create the notifier
|
||||
// Classic Win32 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "ResourceHelper.h"
|
||||
#include "Translations.h"
|
||||
#include <shlobj.h>
|
||||
#include "DesktopToasts/DesktopToasts.h"
|
||||
|
||||
|
||||
void ClosingSettings( HWND hWnd, int flags, int command )
|
||||
@@ -59,11 +60,13 @@ static CSetting g_Settings[]={
|
||||
{L"Update",CSetting::TYPE_GROUP},
|
||||
{L"Language",CSetting::TYPE_STRING,0,0,L"",CSetting::FLAG_SHARED},
|
||||
{L"Update",CSetting::TYPE_BOOL,0,0,1,CSetting::FLAG_SHARED},
|
||||
{L"Nightly",CSetting::TYPE_BOOL,0,0,0,CSetting::FLAG_SHARED},
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
const int SETTING_UPDATE=2;
|
||||
const int SETTING_NIGHTLY=3;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -78,6 +81,7 @@ public:
|
||||
MESSAGE_HANDLER( WM_GETMINMAXINFO, OnGetMinMaxInfo )
|
||||
MESSAGE_HANDLER( WM_CTLCOLORSTATIC, OnColorStatic )
|
||||
COMMAND_HANDLER( IDC_CHECKAUTOCHECK, BN_CLICKED, OnCheckAuto )
|
||||
COMMAND_HANDLER( IDC_CHECKNIGHTLY, BN_CLICKED, OnCheckNightly )
|
||||
COMMAND_HANDLER( IDC_BUTTONCHECKNOW, BN_CLICKED, OnCheckNow )
|
||||
COMMAND_HANDLER( IDC_BUTTONDOWNLOAD, BN_CLICKED, OnDownload )
|
||||
COMMAND_HANDLER( IDC_CHECKDONT, BN_CLICKED, OnDontRemind )
|
||||
@@ -113,6 +117,7 @@ protected:
|
||||
LRESULT OnCancel( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
LRESULT OnColorStatic( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
|
||||
LRESULT OnCheckAuto( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
LRESULT OnCheckNightly( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
LRESULT OnCheckNow( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
LRESULT OnDownload( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
LRESULT OnDontRemind( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
|
||||
@@ -160,6 +165,13 @@ LRESULT CUpdateDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&
|
||||
CheckDlgButton(IDC_CHECKAUTOCHECK,check?BST_CHECKED:BST_UNCHECKED);
|
||||
GetDlgItem(IDC_CHECKAUTOCHECK).EnableWindow(!(g_Settings[SETTING_UPDATE].flags&CSetting::FLAG_LOCKED_MASK));
|
||||
GetDlgItem(IDC_BUTTONCHECKNOW).EnableWindow(!(g_Settings[SETTING_UPDATE].flags&CSetting::FLAG_LOCKED_MASK) || check);
|
||||
|
||||
bool nightly = false;
|
||||
if (g_Settings[SETTING_NIGHTLY].value.vt == VT_I4)
|
||||
nightly = g_Settings[SETTING_NIGHTLY].value.intVal != 0;
|
||||
CheckDlgButton(IDC_CHECKNIGHTLY, nightly ? BST_CHECKED : BST_UNCHECKED);
|
||||
GetDlgItem(IDC_CHECKNIGHTLY).EnableWindow(!(g_Settings[SETTING_NIGHTLY].flags & CSetting::FLAG_LOCKED_MASK) && check);
|
||||
|
||||
UpdateUI();
|
||||
|
||||
return TRUE;
|
||||
@@ -209,6 +221,17 @@ LRESULT CUpdateDlg::OnCheckAuto( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL&
|
||||
bool check=IsDlgButtonChecked(IDC_CHECKAUTOCHECK)==BST_CHECKED;
|
||||
g_Settings[SETTING_UPDATE].value=CComVariant(check?1:0);
|
||||
g_Settings[SETTING_UPDATE].flags&=~CSetting::FLAG_DEFAULT;
|
||||
GetDlgItem(IDC_CHECKNIGHTLY).EnableWindow(check);
|
||||
UpdateUI();
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CUpdateDlg::OnCheckNightly(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
|
||||
{
|
||||
CSettingsLockWrite lock;
|
||||
bool check = IsDlgButtonChecked(IDC_CHECKNIGHTLY) == BST_CHECKED;
|
||||
g_Settings[SETTING_NIGHTLY].value = CComVariant(check ? 1 : 0);
|
||||
g_Settings[SETTING_NIGHTLY].flags &= ~CSetting::FLAG_DEFAULT;
|
||||
UpdateUI();
|
||||
return 0;
|
||||
}
|
||||
@@ -319,7 +342,7 @@ LRESULT CUpdateDlg::OnDontRemind( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL
|
||||
|
||||
LRESULT CUpdateDlg::OnWeb( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
||||
{
|
||||
ShellExecute(m_hWnd,NULL,L"https://github.com/Open-Shell/Open-Shell-Menu",NULL,NULL,SW_SHOWNORMAL);
|
||||
ShellExecute(m_hWnd,NULL,L"https://open-shell.github.io/Open-Shell-Menu/",NULL,NULL,SW_SHOWNORMAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -399,6 +422,9 @@ void CUpdateDlg::UpdateUI( void )
|
||||
|
||||
void CUpdateDlg::Run( void )
|
||||
{
|
||||
if (m_hWnd)
|
||||
return;
|
||||
|
||||
DLGTEMPLATE *pTemplate=LoadDialogEx(IDD_UPDATE);
|
||||
Create(NULL,pTemplate);
|
||||
MSG msg;
|
||||
@@ -457,15 +483,25 @@ protected:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class UpdateToasts : public DesktopToasts
|
||||
{
|
||||
public:
|
||||
UpdateToasts() : DesktopToasts(L"OpenShell.Update") {}
|
||||
|
||||
private:
|
||||
void OnToastActivate(LPCWSTR invokedArgs) override
|
||||
{
|
||||
g_UpdateDlg.Run();
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrCmdLine, int nCmdShow )
|
||||
{
|
||||
INITCOMMONCONTROLSEX init={sizeof(init),ICC_STANDARD_CLASSES};
|
||||
InitCommonControlsEx(&init);
|
||||
/*
|
||||
VersionData data;
|
||||
data.Load(L"D:\\Work\\OpenShell\\Setup\\Final\\update_4.0.4.ver",false);
|
||||
return 0;
|
||||
*/
|
||||
|
||||
// prevent multiple instances from running on the same desktop
|
||||
// the assumption is that multiple desktops for the same user will have different name (but may repeat across users)
|
||||
wchar_t userName[256];
|
||||
@@ -491,8 +527,6 @@ int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrC
|
||||
CString language=GetSettingString(L"Language");
|
||||
ParseTranslations(NULL,language);
|
||||
|
||||
g_Instance=hInstance;
|
||||
|
||||
HINSTANCE resInstance=LoadTranslationDll(language);
|
||||
|
||||
LoadTranslationResources(resInstance,g_LoadDialogs);
|
||||
@@ -504,6 +538,9 @@ int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrC
|
||||
|
||||
COwnerWindow ownerWindow;
|
||||
ownerWindow.Create(NULL,0,0,WS_POPUP);
|
||||
|
||||
UpdateToasts toasts;
|
||||
|
||||
if (wcsstr(lpstrCmdLine,L"-popup")!=NULL)
|
||||
{
|
||||
g_UpdateDlg.UpdateData();
|
||||
@@ -511,57 +548,85 @@ int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrC
|
||||
int sleep=5000-(timeGetTime()-time0);
|
||||
if (sleep>0)
|
||||
Sleep(sleep);
|
||||
HWND balloon=CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|(IsLanguageRTL()?WS_EX_LAYOUTRTL:0),TOOLTIPS_CLASS,NULL,WS_POPUP|TTS_CLOSE|TTS_NOPREFIX,0,0,0,0,NULL,NULL,g_Instance,NULL);
|
||||
SendMessage(balloon,TTM_SETMAXTIPWIDTH,0,500);
|
||||
TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRANSPARENT|TTF_TRACK|(IsLanguageRTL()?TTF_RTLREADING:0U)};
|
||||
tool.uId=1;
|
||||
CString message=LoadStringEx(g_UpdateDlg.HasNewLanguage()?IDS_LANG_NEWVERSION:IDS_NEWVERSION);
|
||||
tool.lpszText=(wchar_t*)(const wchar_t*)message;
|
||||
SendMessage(balloon,TTM_ADDTOOL,0,(LPARAM)&tool);
|
||||
SendMessage(balloon,TTM_SETTITLE,(WPARAM)LoadIcon(g_Instance,MAKEINTRESOURCE(IDI_APPICON)),(LPARAM)(const wchar_t*)LoadStringEx(IDS_UPDATE_TITLE));
|
||||
APPBARDATA appbar={sizeof(appbar)};
|
||||
SHAppBarMessage(ABM_GETTASKBARPOS,&appbar);
|
||||
MONITORINFO info={sizeof(info)};
|
||||
GetMonitorInfo(MonitorFromWindow(appbar.hWnd,MONITOR_DEFAULTTOPRIMARY),&info);
|
||||
SendMessage(balloon,TTM_TRACKPOSITION,0,0);
|
||||
SendMessage(balloon,TTM_TRACKACTIVATE,TRUE,(LPARAM)&tool);
|
||||
RECT rc;
|
||||
GetWindowRect(balloon,&rc);
|
||||
LONG pos;
|
||||
if (appbar.uEdge==ABE_LEFT)
|
||||
pos=MAKELONG(info.rcWork.left,info.rcWork.bottom-rc.bottom+rc.top);
|
||||
else if (appbar.uEdge==ABE_RIGHT)
|
||||
pos=MAKELONG(info.rcWork.right-rc.right+rc.left,info.rcWork.bottom-rc.bottom+rc.top);
|
||||
else if (appbar.uEdge==ABE_TOP)
|
||||
pos=MAKELONG(IsLanguageRTL()?info.rcWork.left:info.rcWork.right-rc.right+rc.left,info.rcWork.top);
|
||||
else
|
||||
pos=MAKELONG(IsLanguageRTL()?info.rcWork.left:info.rcWork.right-rc.right+rc.left,info.rcWork.bottom-rc.bottom+rc.top);
|
||||
SendMessage(balloon,TTM_TRACKPOSITION,0,pos);
|
||||
SetWindowSubclass(balloon,SubclassBalloonProc,0,'CLSH');
|
||||
PlaySound(L"SystemNotification",NULL,SND_APPLICATION|SND_ALIAS|SND_ASYNC|SND_NODEFAULT|SND_SYSTEM);
|
||||
int time0=timeGetTime();
|
||||
while (IsWindowVisible(balloon))
|
||||
|
||||
auto title = LoadStringEx(IDS_UPDATE_TITLE);
|
||||
auto message = LoadStringEx(g_UpdateDlg.HasNewLanguage() ? IDS_LANG_NEWVERSION : IDS_NEWVERSION);
|
||||
|
||||
if (toasts)
|
||||
{
|
||||
if (time0 && (timeGetTime()-time0)>=15000)
|
||||
{
|
||||
time0=0;
|
||||
TOOLINFO tool={sizeof(tool)};
|
||||
tool.uId=1;
|
||||
SendMessage(balloon,TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool);
|
||||
}
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg,0,0,0,PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
Sleep(10);
|
||||
toasts.DisplaySimpleToast(title, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
HWND balloon = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW | (IsLanguageRTL() ? WS_EX_LAYOUTRTL : 0), TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_CLOSE | TTS_NOPREFIX, 0, 0, 0, 0, NULL, NULL, g_Instance, NULL);
|
||||
SendMessage(balloon, TTM_SETMAXTIPWIDTH, 0, 500);
|
||||
TOOLINFO tool = { sizeof(tool),TTF_ABSOLUTE | TTF_TRANSPARENT | TTF_TRACK | (IsLanguageRTL() ? TTF_RTLREADING : 0U) };
|
||||
tool.uId = 1;
|
||||
tool.lpszText = (wchar_t*)(const wchar_t*)message;
|
||||
SendMessage(balloon, TTM_ADDTOOL, 0, (LPARAM)&tool);
|
||||
SendMessage(balloon, TTM_SETTITLE, (WPARAM)LoadIcon(g_Instance, MAKEINTRESOURCE(IDI_APPICON)), (LPARAM)(const wchar_t*)title);
|
||||
APPBARDATA appbar = { sizeof(appbar) };
|
||||
SHAppBarMessage(ABM_GETTASKBARPOS, &appbar);
|
||||
MONITORINFO info = { sizeof(info) };
|
||||
GetMonitorInfo(MonitorFromWindow(appbar.hWnd, MONITOR_DEFAULTTOPRIMARY), &info);
|
||||
SendMessage(balloon, TTM_TRACKPOSITION, 0, 0);
|
||||
SendMessage(balloon, TTM_TRACKACTIVATE, TRUE, (LPARAM)&tool);
|
||||
RECT rc;
|
||||
GetWindowRect(balloon, &rc);
|
||||
LONG pos;
|
||||
if (appbar.uEdge == ABE_LEFT)
|
||||
pos = MAKELONG(info.rcWork.left, info.rcWork.bottom - rc.bottom + rc.top);
|
||||
else if (appbar.uEdge == ABE_RIGHT)
|
||||
pos = MAKELONG(info.rcWork.right - rc.right + rc.left, info.rcWork.bottom - rc.bottom + rc.top);
|
||||
else if (appbar.uEdge == ABE_TOP)
|
||||
pos = MAKELONG(IsLanguageRTL() ? info.rcWork.left : info.rcWork.right - rc.right + rc.left, info.rcWork.top);
|
||||
else
|
||||
pos = MAKELONG(IsLanguageRTL() ? info.rcWork.left : info.rcWork.right - rc.right + rc.left, info.rcWork.bottom - rc.bottom + rc.top);
|
||||
SendMessage(balloon, TTM_TRACKPOSITION, 0, pos);
|
||||
SetWindowSubclass(balloon, SubclassBalloonProc, 0, 'CLSH');
|
||||
PlaySound(L"SystemNotification", NULL, SND_APPLICATION | SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_SYSTEM);
|
||||
int time0 = timeGetTime();
|
||||
while (IsWindowVisible(balloon))
|
||||
{
|
||||
if (time0 && (timeGetTime() - time0) >= 15000)
|
||||
{
|
||||
time0 = 0;
|
||||
TOOLINFO tool = { sizeof(tool) };
|
||||
tool.uId = 1;
|
||||
SendMessage(balloon, TTM_TRACKACTIVATE, FALSE, (LPARAM)&tool);
|
||||
}
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (wcsstr(lpstrCmdLine, L"-ToastActivated"))
|
||||
{
|
||||
g_UpdateDlg.UpdateData();
|
||||
// dialog will be shown once toast is activated (UpdateToasts::OnToastActivate)
|
||||
}
|
||||
else
|
||||
{
|
||||
g_UpdateDlg.Run();
|
||||
}
|
||||
|
||||
// process messages for a while
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
ownerWindow.DestroyWindow();
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
|
||||
@@ -123,22 +123,24 @@ END
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_UPDATE DIALOGEX 0, 0, 316, 181
|
||||
IDD_UPDATE DIALOGEX 0, 0, 316, 200
|
||||
STYLE DS_SETFONT | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
|
||||
CAPTION "Open-Shell Update"
|
||||
FONT 9, "Segoe UI", 400, 0, 0x0
|
||||
BEGIN
|
||||
CONTROL "Automatically check for new versions",IDC_CHECKAUTOCHECK,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,129,10
|
||||
PUSHBUTTON "Check now",IDC_BUTTONCHECKNOW,7,17,50,14
|
||||
LTEXT "message",IDC_STATICLATEST,7,33,302,10,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_EDITTEXT,7,45,302,97,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE | WS_VSCROLL
|
||||
PUSHBUTTON "Download",IDC_BUTTONDOWNLOAD,7,144,50,14,NOT WS_VISIBLE
|
||||
CONTROL "Check for nightly builds",IDC_CHECKNIGHTLY,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,19,151,10
|
||||
PUSHBUTTON "Check now",IDC_BUTTONCHECKNOW,7,34,50,14
|
||||
LTEXT "message",IDC_STATICLATEST,7,48,302,10,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_EDITTEXT,7,60,302,97,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE | WS_VSCROLL
|
||||
PUSHBUTTON "Download",IDC_BUTTONDOWNLOAD,7,161,50,14,NOT WS_VISIBLE
|
||||
CONTROL "Don't remind me again about this version",IDC_CHECKDONT,
|
||||
"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,61,144,141,14
|
||||
CONTROL "<a>https://github.com/Open-Shell/Open-Shell-Menu</a>",IDC_LINKWEB,"SysLink",WS_TABSTOP,7,164,66,10,WS_EX_TRANSPARENT
|
||||
DEFPUSHBUTTON "OK",IDOK,202,160,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,259,160,50,14
|
||||
"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,61,161,141,14
|
||||
CONTROL "<a>Open-Shell-Menu</a>",IDC_LINKWEB,"SysLink",WS_TABSTOP,7,181,66,10,WS_EX_TRANSPARENT
|
||||
DEFPUSHBUTTON "OK",IDOK,202,177,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,259,177,50,14
|
||||
END
|
||||
|
||||
|
||||
@@ -155,7 +157,7 @@ BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 309
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 174
|
||||
BOTTOMMARGIN, 191
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define IDC_CHECKDONT 1004
|
||||
#define IDC_BUTTONCHECKNOW 1005
|
||||
#define IDC_CHECKAUTOCHECK 1006
|
||||
#define IDC_CHECKNIGHTLY 1007
|
||||
#define IDD_UPDATE 6001
|
||||
#define IDS_UPDATED 6001
|
||||
#define IDS_OUTOFDATE 6002
|
||||
@@ -21,7 +22,7 @@
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 227
|
||||
#define _APS_NEXT_RESOURCE_VALUE 228
|
||||
#define _APS_NEXT_COMMAND_VALUE 32769
|
||||
#define _APS_NEXT_CONTROL_VALUE 262
|
||||
#define _APS_NEXT_SYMED_VALUE 106
|
||||
|
||||
Reference in New Issue
Block a user