Files
Open-Shell-Menu/Src/Update/Update.cpp
ge0rdi 4883d13950 Update: Support for automatic updates
Use Github REST API to get info about latest release (version,
changelog, installer url).
2020-09-24 18:52:24 +02:00

565 lines
18 KiB
C++

// Classic Shell (c) 2009-2017, Ivo Beltchev
// Open-Shell (c) 2017-2018, The Open-Shell Team
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
#define STRICT_TYPED_ITEMIDS
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <windows.h>
#include <atlbase.h>
#include <atlwin.h>
#include <atlstr.h>
#include "resource.h"
#include "StringUtils.h"
#include "Settings.h"
#include "SettingsUIHelper.h"
#include "DownloadHelper.h"
#include "ResourceHelper.h"
#include "Translations.h"
#include <shlobj.h>
void ClosingSettings( HWND hWnd, int flags, int command )
{
}
void SettingChangedCallback( const CSetting *pSetting )
{
}
void UpgradeSettings( bool bShared )
{
}
void UpdateSettings( void )
{
CRegKey regKey;
wchar_t language[100]=L"";
if (regKey.Open(HKEY_LOCAL_MACHINE,L"Software\\OpenShell\\OpenShell",KEY_READ|KEY_WOW64_64KEY)==ERROR_SUCCESS)
{
ULONG size=_countof(language);
if (regKey.QueryStringValue(L"DefaultLanguage",language,&size)!=ERROR_SUCCESS)
language[0]=0;
}
UpdateSetting(L"Language",language,false);
}
const wchar_t *GetDocRelativePath( void )
{
return NULL;
}
static int g_LoadDialogs[]=
{
IDD_UPDATE,0x04000000,
IDD_PROGRESS,0x04000004,
0
};
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},
{NULL}
};
const int SETTING_UPDATE=2;
///////////////////////////////////////////////////////////////////////////////
class CUpdateDlg: public CResizeableDlg<CUpdateDlg>
{
public:
CUpdateDlg( void ) { m_Version=GetVersionEx(g_Instance); m_Data.Clear(); m_Data.newVersion=0xFFFFFFFF; m_Font=NULL; }
BEGIN_MSG_MAP( CUpdateDlg )
MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
MESSAGE_HANDLER( WM_SIZE, OnSize )
MESSAGE_HANDLER( WM_GETMINMAXINFO, OnGetMinMaxInfo )
MESSAGE_HANDLER( WM_CTLCOLORSTATIC, OnColorStatic )
COMMAND_HANDLER( IDC_CHECKAUTOCHECK, BN_CLICKED, OnCheckAuto )
COMMAND_HANDLER( IDC_BUTTONCHECKNOW, BN_CLICKED, OnCheckNow )
COMMAND_HANDLER( IDC_BUTTONDOWNLOAD, BN_CLICKED, OnDownload )
COMMAND_HANDLER( IDC_CHECKDONT, BN_CLICKED, OnDontRemind )
COMMAND_HANDLER( IDOK, BN_CLICKED, OnOK )
COMMAND_HANDLER( IDCANCEL, BN_CLICKED, OnCancel )
NOTIFY_HANDLER( IDC_LINKWEB, NM_CLICK, OnWeb )
NOTIFY_HANDLER( IDC_LINKWEB, NM_RETURN, OnWeb )
END_MSG_MAP()
BEGIN_RESIZE_MAP
RESIZE_CONTROL(IDC_STATICLATEST,MOVE_SIZE_X)
RESIZE_CONTROL(IDC_EDITTEXT,MOVE_SIZE_X|MOVE_SIZE_Y)
RESIZE_CONTROL(IDC_BUTTONDOWNLOAD,MOVE_MOVE_Y)
RESIZE_CONTROL(IDC_CHECKDONT,MOVE_MOVE_Y)
RESIZE_CONTROL(IDC_LINKWEB,MOVE_MOVE_Y)
RESIZE_CONTROL(IDOK,MOVE_MOVE_X|MOVE_MOVE_Y)
RESIZE_CONTROL(IDCANCEL,MOVE_MOVE_X|MOVE_MOVE_Y)
END_RESIZE_MAP
void Run( void );
void UpdateData( void );
bool HasNewLanguage( void ) { return (m_Data.bNewLanguage && !m_Data.bIgnoreLanguage) && !(m_Data.bNewVersion && !m_Data.bIgnoreVersion); }
protected:
// Handler prototypes:
// LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
// LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
// LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
LRESULT OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
LRESULT OnSize( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
LRESULT OnOK( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled );
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 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 );
LRESULT OnWeb( int idCtrl, LPNMHDR pnmh, BOOL& bHandled );
private:
CWindow m_Tooltip;
VersionData m_Data;
DWORD m_Version;
HFONT m_Font;
void UpdateUI( void );
static void NewVersionCallback( VersionData &data );
};
static CUpdateDlg g_UpdateDlg;
LRESULT CUpdateDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
InitResize(MOVE_MODAL);
HICON icon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_APPICON),IMAGE_ICON,GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),LR_DEFAULTCOLOR);
SendMessage(WM_SETICON,ICON_BIG,(LPARAM)icon);
icon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_APPICON),IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),LR_DEFAULTCOLOR);
SendMessage(WM_SETICON,ICON_SMALL,(LPARAM)icon);
SetDlgItemText(IDC_STATICLATEST,L"");
HDC hdc=::GetDC(NULL);
int dpi=GetDeviceCaps(hdc,LOGPIXELSY);
::ReleaseDC(NULL,hdc);
m_Font=CreateFont(-9*dpi/72,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH,L"Consolas");
if (m_Font)
GetDlgItem(IDC_EDITTEXT).SetFont(m_Font);
m_Tooltip.Create(TOOLTIPS_CLASS,m_hWnd,NULL,NULL,WS_POPUP|TTS_NOPREFIX);
TOOLINFO tool={sizeof(tool),TTF_SUBCLASS|TTF_IDISHWND,m_hWnd,(UINT_PTR)GetDlgItem(IDC_LINKWEB).m_hWnd};
CString str=LoadStringEx(IDS_WEBSITE_TIP);
tool.lpszText=(LPWSTR)(LPCWSTR)str;
m_Tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool);
bool check=true;
if (g_Settings[SETTING_UPDATE].value.vt==VT_I4)
check=g_Settings[SETTING_UPDATE].value.intVal!=0;
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);
UpdateUI();
return TRUE;
}
LRESULT CUpdateDlg::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if (m_Font) DeleteObject(m_Font);
return 0;
}
LRESULT CUpdateDlg::OnSize( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
CResizeableDlg<CUpdateDlg>::OnSize();
return 0;
}
LRESULT CUpdateDlg::OnOK( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
SaveSettings();
DestroyWindow();
return 0;
}
LRESULT CUpdateDlg::OnCancel( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
DestroyWindow();
return 0;
}
LRESULT CUpdateDlg::OnColorStatic( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if ((m_Data.bNewVersion || m_Data.bNewLanguage) && lParam==(LPARAM)GetDlgItem(IDC_STATICLATEST).m_hWnd)
{
HDC hdc=(HDC)wParam;
SetTextColor(hdc,0xFF);
SetBkMode(hdc,TRANSPARENT);
return (LRESULT)GetSysColorBrush(COLOR_3DFACE);
}
bHandled=FALSE;
return 0;
}
LRESULT CUpdateDlg::OnCheckAuto( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
CSettingsLockWrite lock;
bool check=IsDlgButtonChecked(IDC_CHECKAUTOCHECK)==BST_CHECKED;
g_Settings[SETTING_UPDATE].value=CComVariant(check?1:0);
g_Settings[SETTING_UPDATE].flags&=~CSetting::FLAG_DEFAULT;
UpdateUI();
return 0;
}
LRESULT CUpdateDlg::OnCheckNow( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
UpdateData();
UpdateUI();
return 0;
}
static HRESULT CALLBACK TaskDialogCallbackProc( HWND hwnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData )
{
if (uNotification==TDN_HYPERLINK_CLICKED)
{
ShellExecute(hwnd,L"open",(const wchar_t*)lParam,NULL,NULL,SW_SHOWNORMAL);
}
return S_OK;
}
LRESULT CUpdateDlg::OnDownload( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
if (m_Data.bNewVersion)
{
if (!m_Data.downloadUrl.IsEmpty())
{
CString fname, error;
DWORD res=DownloadNewVersion(m_hWnd,COMPONENT_UPDATE,m_Data.downloadUrl,m_Data.downloadSigner,fname,error);
if (res==2)
return 0;
if (res)
{
SetDlgItemText(IDC_STATICLATEST,L"");
PIDLIST_ABSOLUTE pidl;
if (SUCCEEDED(SHParseDisplayName(fname,NULL,&pidl,0,NULL)))
{
HRESULT hr=SHOpenFolderAndSelectItems(pidl,0,NULL,0);
ILFree(pidl);
if (SUCCEEDED(hr))
return 0;
}
ShellExecute(NULL,NULL,fname,NULL,NULL,SW_SHOWNORMAL);
}
else
{
error+=LoadStringEx(IDS_DOWNLOAD_TIP)+L"\r\n\r\n"+m_Data.updateLink;
TASKDIALOGCONFIG task={sizeof(task),m_hWnd,NULL,TDF_ENABLE_HYPERLINKS|TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_HICON_MAIN,TDCBF_OK_BUTTON};
CString title=LoadStringEx(IDS_UPDATE_TITLE);
task.pszWindowTitle=title;
task.pszContent=error;
task.hMainIcon=LoadIcon(NULL,IDI_ERROR);
task.pfCallback=TaskDialogCallbackProc;
TaskDialogIndirect(&task,NULL,NULL,NULL);
}
}
}
else if (m_Data.bNewLanguage)
{
for (std::vector<LanguageVersionData>::const_iterator it=m_Data.languages.begin();it!=m_Data.languages.end();++it)
{
if (_wcsicmp(m_Data.newLanguage,it->language)==0)
{
CString error;
DWORD res=DownloadLanguageDll(m_hWnd,COMPONENT_UPDATE,*it,error);
if (res==2)
return 0;
if (res)
{
MessageBox(LoadStringEx(it->bBasic?IDS_LANGUAGE_SUCCESS2:IDS_LANGUAGE_SUCCESS),LoadStringEx(IDS_UPDATE_TITLE),MB_OK|(it->bBasic?MB_ICONWARNING:MB_ICONINFORMATION));
SetDlgItemText(IDC_STATICLATEST,L"");
}
else
{
error+=LoadStringEx(IDS_DOWNLOAD_TIP)+L"\r\n\r\n"+m_Data.languageLink;
TASKDIALOGCONFIG task={sizeof(task),m_hWnd,NULL,TDF_ENABLE_HYPERLINKS|TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_HICON_MAIN,TDCBF_OK_BUTTON};
CString title=LoadStringEx(IDS_UPDATE_TITLE);
task.pszWindowTitle=title;
task.pszContent=error;
task.hMainIcon=LoadIcon(NULL,IDI_ERROR);
task.pfCallback=TaskDialogCallbackProc;
TaskDialogIndirect(&task,NULL,NULL,NULL);
}
return 0;
}
}
Assert(0); // NEWLanguage is not in the list
}
return 0;
}
LRESULT CUpdateDlg::OnDontRemind( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\OpenShell",KEY_READ|KEY_WRITE)!=ERROR_SUCCESS)
regKey.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\OpenShell");
if (m_Data.bNewVersion)
{
m_Data.bIgnoreVersion=(IsDlgButtonChecked(IDC_CHECKDONT)==BST_CHECKED);
regKey.SetDWORDValue(L"RemindedVersion",m_Data.bIgnoreVersion?m_Data.newVersion:0);
}
else if (m_Data.bNewLanguage)
{
m_Data.bIgnoreLanguage=(IsDlgButtonChecked(IDC_CHECKDONT)==BST_CHECKED);
regKey.SetDWORDValue(L"RemindedLangVersion",m_Data.bIgnoreLanguage?m_Data.encodedLangVersion:0);
}
return 0;
}
LRESULT CUpdateDlg::OnWeb( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
ShellExecute(m_hWnd,NULL,L"https://open-shell.github.io/Open-Shell-Menu/",NULL,NULL,SW_SHOWNORMAL);
return 0;
}
void CUpdateDlg::NewVersionCallback( VersionData &data )
{
g_UpdateDlg.m_Data.Swap(data);
}
void CUpdateDlg::UpdateData( void )
{
if (!CheckForNewVersion(m_hWnd,COMPONENT_UPDATE,CHECK_UPDATE,NewVersionCallback))
{
m_Data.Clear();
}
}
void CUpdateDlg::UpdateUI( void )
{
if (m_Data.bValid)
{
if (m_Data.bNewVersion)
{
SetDlgItemText(IDC_STATICLATEST,LoadStringEx(IDS_OUTOFDATE));
SetDlgItemText(IDC_EDITTEXT,m_Data.news);
GetDlgItem(IDC_EDITTEXT).ShowWindow(SW_SHOW);
GetDlgItem(IDC_BUTTONDOWNLOAD).ShowWindow(SW_SHOW);
bool check=true;
if (g_Settings[SETTING_UPDATE].value.vt==VT_I4)
check=g_Settings[SETTING_UPDATE].value.intVal!=0;
GetDlgItem(IDC_CHECKDONT).ShowWindow(check?SW_SHOW:SW_HIDE);
CheckDlgButton(IDC_CHECKDONT,m_Data.bIgnoreVersion?BST_CHECKED:BST_UNCHECKED);
TOOLINFO tool={sizeof(tool),TTF_SUBCLASS|TTF_IDISHWND,m_hWnd,(UINT_PTR)GetDlgItem(IDC_BUTTONDOWNLOAD).m_hWnd};
tool.lpszText=(LPWSTR)(LPCWSTR)m_Data.downloadUrl;
m_Tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool);
}
else if (m_Data.bNewLanguage)
{
SetDlgItemText(IDC_STATICLATEST,LoadStringEx(IDS_LANG_OUTOFDATE));
SetDlgItemText(IDC_EDITTEXT,L"");
GetDlgItem(IDC_EDITTEXT).ShowWindow(SW_HIDE);
GetDlgItem(IDC_BUTTONDOWNLOAD).ShowWindow(SW_SHOW);
bool check=true;
if (g_Settings[SETTING_UPDATE].value.vt==VT_I4)
check=g_Settings[SETTING_UPDATE].value.intVal!=0;
GetDlgItem(IDC_CHECKDONT).ShowWindow(check?SW_SHOW:SW_HIDE);
CheckDlgButton(IDC_CHECKDONT,m_Data.bIgnoreLanguage?BST_CHECKED:BST_UNCHECKED);
TOOLINFO tool={sizeof(tool),TTF_SUBCLASS|TTF_IDISHWND,m_hWnd,(UINT_PTR)GetDlgItem(IDC_BUTTONDOWNLOAD).m_hWnd};
for (std::vector<LanguageVersionData>::const_iterator it=m_Data.languages.begin();it!=m_Data.languages.end();++it)
{
if (_wcsicmp(m_Data.newLanguage,it->language)==0)
{
tool.lpszText=(LPWSTR)(LPCWSTR)it->url;
break;
}
}
m_Tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool);
}
else
{
SetDlgItemText(IDC_STATICLATEST,LoadStringEx(IDS_UPDATED));
SetDlgItemText(IDC_EDITTEXT,L"");
GetDlgItem(IDC_EDITTEXT).ShowWindow(SW_HIDE);
GetDlgItem(IDC_BUTTONDOWNLOAD).ShowWindow(SW_HIDE);
GetDlgItem(IDC_CHECKDONT).ShowWindow(SW_HIDE);
}
}
else
{
SetDlgItemText(IDC_STATICLATEST,(m_Data.newVersion==0)?LoadStringEx(IDS_UPDATE_FAIL).GetString():L"");
SetDlgItemText(IDC_EDITTEXT,L"");
GetDlgItem(IDC_EDITTEXT).ShowWindow(SW_HIDE);
GetDlgItem(IDC_BUTTONDOWNLOAD).ShowWindow(SW_HIDE);
GetDlgItem(IDC_CHECKDONT).ShowWindow(SW_HIDE);
}
Invalidate();
}
void CUpdateDlg::Run( void )
{
DLGTEMPLATE *pTemplate=LoadDialogEx(IDD_UPDATE);
Create(NULL,pTemplate);
MSG msg;
while (m_hWnd && GetMessage(&msg,0,0,0))
{
if ((msg.hwnd==m_hWnd || IsChild(msg.hwnd)) && IsDialogMessage(&msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK SubclassBalloonProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==WM_LBUTTONUP)
{
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
if (SendMessage(hWnd,TTM_GETCURRENTTOOL,0,0))
{
TOOLINFO tool={sizeof(tool)};
tool.uId=1;
SendMessage(hWnd,TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool);
g_UpdateDlg.Run();
}
return res;
}
if (uMsg==WM_MOUSEACTIVATE)
return MA_NOACTIVATE;
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
///////////////////////////////////////////////////////////////////////////////
class COwnerWindow: public CWindowImpl<COwnerWindow>
{
public:
DECLARE_WND_CLASS_EX(L"OpenShellUpdate.COwnerWindow",0,COLOR_MENU)
// message handlers
BEGIN_MSG_MAP( COwnerWindow )
MESSAGE_HANDLER( WM_CLEAR, OnClear )
END_MSG_MAP()
protected:
LRESULT OnClear( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if (g_UpdateDlg)
{
g_UpdateDlg.PostMessage(WM_CLOSE);
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////////
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrCmdLine, int nCmdShow )
{
INITCOMMONCONTROLSEX init={sizeof(init),ICC_STANDARD_CLASSES};
InitCommonControlsEx(&init);
// 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];
DWORD len=_countof(userName);
GetUserName(userName,&len);
len=0;
HANDLE desktop=GetThreadDesktop(GetCurrentThreadId());
GetUserObjectInformation(desktop,UOI_NAME,NULL,0,&len);
wchar_t *deskName=(wchar_t*)malloc(len);
GetUserObjectInformation(desktop,UOI_NAME,deskName,len,&len);
wchar_t mutexName[1024];
Sprintf(mutexName,_countof(mutexName),L"OpenShellUpdate.Mutex.%s.%s",userName,deskName);
free(deskName);
HANDLE hMutex=CreateMutex(NULL,TRUE,mutexName);
if (GetLastError()==ERROR_ALREADY_EXISTS || GetLastError()==ERROR_ACCESS_DENIED)
return 0;
CoInitialize(NULL);
g_Instance=hInstance;
InitSettings(g_Settings,COMPONENT_UPDATE,NULL);
CString language=GetSettingString(L"Language");
ParseTranslations(NULL,language);
g_Instance=hInstance;
HINSTANCE resInstance=LoadTranslationDll(language);
LoadTranslationResources(resInstance,g_LoadDialogs);
if (resInstance)
FreeLibrary(resInstance);
int time0=timeGetTime();
COwnerWindow ownerWindow;
ownerWindow.Create(NULL,0,0,WS_POPUP);
if (wcsstr(lpstrCmdLine,L"-popup")!=NULL)
{
g_UpdateDlg.UpdateData();
// wait at least 5 seconds before showing the balloon
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))
{
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
{
g_UpdateDlg.Run();
}
ownerWindow.DestroyWindow();
CoUninitialize();
return 0;
}