xref: /reactos/base/applications/rapps/appinfo.cpp (revision ac0bcf4a)
1 /*
2  * PROJECT:     ReactOS Applications Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Classes for working with available applications
5  * COPYRIGHT:   Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
6  *              Copyright 2020 He Yang (1160386205@qq.com)
7  *              Copyright 2021-2023 Mark Jansen <mark.jansen@reactos.org>
8  */
9 
10 #include "rapps.h"
11 #include "appview.h"
12 
13 CAppInfo::CAppInfo(const CStringW &Identifier, AppsCategories Category)
14     : szIdentifier(Identifier), iCategory(Category)
15 {
16 }
17 
18 CAppInfo::~CAppInfo()
19 {
20 }
21 
22 CAvailableApplicationInfo::CAvailableApplicationInfo(
23     CConfigParser *Parser,
24     const CStringW &PkgName,
25     AppsCategories Category,
26     const CPathW &BasePath)
27     : CAppInfo(PkgName, Category), m_Parser(Parser), m_ScrnshotRetrieved(false), m_LanguagesLoaded(false)
28 {
29     m_Parser->GetString(L"Name", szDisplayName);
30     m_Parser->GetString(L"Version", szDisplayVersion);
31     m_Parser->GetString(L"URLDownload", m_szUrlDownload);
32     m_Parser->GetString(L"Description", szComments);
33 
34     CPathW IconPath = BasePath;
35     IconPath += L"icons";
36 
37     CStringW IconName;
38     if (m_Parser->GetString(L"Icon", IconName))
39     {
40         IconPath += IconName;
41     }
42     else
43     {
44         // inifile.ico
45         IconPath += (szIdentifier + L".ico");
46     }
47 
48     if (PathFileExistsW(IconPath))
49     {
50         szDisplayIcon = (LPCWSTR)IconPath;
51     }
52 
53     INT iSizeBytes;
54 
55     if (m_Parser->GetInt(L"SizeBytes", iSizeBytes))
56     {
57         StrFormatByteSizeW(iSizeBytes, m_szSize.GetBuffer(MAX_PATH), MAX_PATH);
58         m_szSize.ReleaseBuffer();
59     }
60 
61     m_Parser->GetString(L"URLSite", m_szUrlSite);
62 }
63 
64 CAvailableApplicationInfo::~CAvailableApplicationInfo()
65 {
66     delete m_Parser;
67 }
68 
69 VOID
70 CAvailableApplicationInfo::ShowAppInfo(CAppRichEdit *RichEdit)
71 {
72     RichEdit->SetText(szDisplayName, CFE_BOLD);
73     InsertVersionInfo(RichEdit);
74     RichEdit->LoadAndInsertText(IDS_AINFO_LICENSE, LicenseString(), 0);
75     InsertLanguageInfo(RichEdit);
76 
77     RichEdit->LoadAndInsertText(IDS_AINFO_SIZE, m_szSize, 0);
78     RichEdit->LoadAndInsertText(IDS_AINFO_URLSITE, m_szUrlSite, CFE_LINK);
79     RichEdit->LoadAndInsertText(IDS_AINFO_DESCRIPTION, szComments, 0);
80     RichEdit->LoadAndInsertText(IDS_AINFO_URLDOWNLOAD, m_szUrlDownload, CFE_LINK);
81     RichEdit->LoadAndInsertText(IDS_AINFO_PACKAGE_NAME, szIdentifier, 0);
82 }
83 
84 int
85 CompareVersion(const CStringW &left, const CStringW &right)
86 {
87     int nLeft = 0, nRight = 0;
88 
89     while (true)
90     {
91         CStringW leftPart = left.Tokenize(L".", nLeft);
92         CStringW rightPart = right.Tokenize(L".", nRight);
93 
94         if (leftPart.IsEmpty() && rightPart.IsEmpty())
95             return 0;
96         if (leftPart.IsEmpty())
97             return -1;
98         if (rightPart.IsEmpty())
99             return 1;
100 
101         int leftVal, rightVal;
102 
103         if (!StrToIntExW(leftPart, STIF_DEFAULT, &leftVal))
104             leftVal = 0;
105         if (!StrToIntExW(rightPart, STIF_DEFAULT, &rightVal))
106             rightVal = 0;
107 
108         if (leftVal > rightVal)
109             return 1;
110         if (rightVal < leftVal)
111             return -1;
112     }
113 }
114 
115 VOID
116 CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
117 {
118     CStringW szRegName;
119     m_Parser->GetString(DB_REGNAME, szRegName);
120 
121     BOOL bIsInstalled = ::GetInstalledVersion(NULL, szRegName) || ::GetInstalledVersion(NULL, szDisplayName);
122     if (bIsInstalled)
123     {
124         CStringW szInstalledVersion;
125         CStringW szNameVersion = szDisplayName + L" " + szDisplayVersion;
126         BOOL bHasInstalledVersion = ::GetInstalledVersion(&szInstalledVersion, szRegName) ||
127                                     ::GetInstalledVersion(&szInstalledVersion, szDisplayName) ||
128                                     ::GetInstalledVersion(&szInstalledVersion, szNameVersion);
129 
130         if (bHasInstalledVersion)
131         {
132             BOOL bHasUpdate = CompareVersion(szInstalledVersion, szDisplayVersion) < 0;
133             if (bHasUpdate)
134             {
135                 RichEdit->LoadAndInsertText(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
136                 RichEdit->LoadAndInsertText(IDS_AINFO_VERSION, szInstalledVersion, 0);
137             }
138             else
139             {
140                 RichEdit->LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC);
141             }
142         }
143         else
144         {
145             RichEdit->LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC);
146         }
147     }
148     else
149     {
150         RichEdit->LoadAndInsertText(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
151     }
152 
153     RichEdit->LoadAndInsertText(IDS_AINFO_AVAILABLEVERSION, szDisplayVersion, 0);
154 }
155 
156 CStringW
157 CAvailableApplicationInfo::LicenseString()
158 {
159     INT IntBuffer;
160     m_Parser->GetInt(L"LicenseType", IntBuffer);
161     CStringW szLicenseString;
162     m_Parser->GetString(L"License", szLicenseString);
163     LicenseType licenseType;
164 
165     if (IsKnownLicenseType(IntBuffer))
166     {
167         licenseType = static_cast<LicenseType>(IntBuffer);
168     }
169     else
170     {
171         licenseType = LICENSE_NONE;
172         if (szLicenseString.CompareNoCase(L"Freeware") == 0)
173         {
174             licenseType = LICENSE_FREEWARE;
175             szLicenseString = L"";
176         }
177     }
178 
179     CStringW szLicense;
180     switch (licenseType)
181     {
182         case LICENSE_OPENSOURCE:
183             szLicense.LoadStringW(IDS_LICENSE_OPENSOURCE);
184             break;
185         case LICENSE_FREEWARE:
186             szLicense.LoadStringW(IDS_LICENSE_FREEWARE);
187             break;
188         case LICENSE_TRIAL:
189             szLicense.LoadStringW(IDS_LICENSE_TRIAL);
190             break;
191         default:
192             return szLicenseString;
193     }
194 
195     if (!szLicenseString.IsEmpty())
196         szLicense += L" (" + szLicenseString + L")";
197     return szLicense;
198 }
199 
200 VOID
201 CAvailableApplicationInfo::InsertLanguageInfo(CAppRichEdit *RichEdit)
202 {
203     if (!m_LanguagesLoaded)
204     {
205         RetrieveLanguages();
206     }
207 
208     if (m_LanguageLCIDs.GetSize() == 0)
209     {
210         return;
211     }
212 
213     const INT nTranslations = m_LanguageLCIDs.GetSize();
214     CStringW szLangInfo;
215     CStringW szLoadedTextAvailability;
216     CStringW szLoadedAInfoText;
217 
218     szLoadedAInfoText.LoadStringW(IDS_AINFO_LANGUAGES);
219 
220     const LCID lcEnglish = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
221     if (m_LanguageLCIDs.Find(GetUserDefaultLCID()) >= 0)
222     {
223         szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_AVAILABLE_TRANSLATION);
224         if (nTranslations > 1)
225         {
226             CStringW buf;
227             buf.LoadStringW(IDS_LANGUAGE_MORE_PLACEHOLDER);
228             szLangInfo.Format(buf, nTranslations - 1);
229         }
230         else
231         {
232             szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
233             szLangInfo = L" (" + szLangInfo + L")";
234         }
235     }
236     else if (m_LanguageLCIDs.Find(lcEnglish) >= 0)
237     {
238         szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_ENGLISH_TRANSLATION);
239         if (nTranslations > 1)
240         {
241             CStringW buf;
242             buf.LoadStringW(IDS_LANGUAGE_AVAILABLE_PLACEHOLDER);
243             szLangInfo.Format(buf, nTranslations - 1);
244         }
245         else
246         {
247             szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
248             szLangInfo = L" (" + szLangInfo + L")";
249         }
250     }
251     else
252     {
253         szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_NO_TRANSLATION);
254     }
255 
256     RichEdit->InsertText(szLoadedAInfoText, CFE_BOLD);
257     RichEdit->InsertText(szLoadedTextAvailability, NULL);
258     RichEdit->InsertText(szLangInfo, CFE_ITALIC);
259 }
260 
261 VOID
262 CAvailableApplicationInfo::RetrieveLanguages()
263 {
264     m_LanguagesLoaded = true;
265 
266     CStringW szBuffer;
267     if (!m_Parser->GetString(L"Languages", szBuffer))
268     {
269         return;
270     }
271 
272     // Parse parameter string
273     int iIndex = 0;
274     while (true)
275     {
276         CStringW szLocale = szBuffer.Tokenize(L"|", iIndex);
277         if (szLocale.IsEmpty())
278             break;
279 
280         szLocale = L"0x" + szLocale;
281 
282         INT iLCID;
283         if (StrToIntExW(szLocale, STIF_SUPPORT_HEX, &iLCID))
284         {
285             m_LanguageLCIDs.Add(static_cast<LCID>(iLCID));
286         }
287     }
288 }
289 
290 BOOL
291 CAvailableApplicationInfo::Valid() const
292 {
293     return !szDisplayName.IsEmpty() && !m_szUrlDownload.IsEmpty();
294 }
295 
296 BOOL
297 CAvailableApplicationInfo::CanModify()
298 {
299     return FALSE;
300 }
301 
302 BOOL
303 CAvailableApplicationInfo::RetrieveIcon(CStringW &Path) const
304 {
305     Path = szDisplayIcon;
306     return !Path.IsEmpty();
307 }
308 
309 #define MAX_SCRNSHOT_NUM 16
310 BOOL
311 CAvailableApplicationInfo::RetrieveScreenshot(CStringW &Path)
312 {
313     if (!m_ScrnshotRetrieved)
314     {
315         static_assert(MAX_SCRNSHOT_NUM < 10000, "MAX_SCRNSHOT_NUM is too big");
316         for (int i = 0; i < MAX_SCRNSHOT_NUM; i++)
317         {
318             CStringW ScrnshotField;
319             ScrnshotField.Format(L"Screenshot%d", i + 1);
320             CStringW ScrnshotLocation;
321             if (!m_Parser->GetString(ScrnshotField, ScrnshotLocation))
322             {
323                 // We stop at the first screenshot not found,
324                 // so screenshots _have_ to be consecutive
325                 break;
326             }
327 
328             if (PathIsURLW(ScrnshotLocation.GetString()))
329             {
330                 m_szScrnshotLocation.Add(ScrnshotLocation);
331             }
332         }
333         m_ScrnshotRetrieved = true;
334     }
335 
336     if (m_szScrnshotLocation.GetSize() > 0)
337     {
338         Path = m_szScrnshotLocation[0];
339     }
340 
341     return !Path.IsEmpty();
342 }
343 
344 VOID
345 CAvailableApplicationInfo::GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const
346 {
347     Url = m_szUrlDownload;
348     m_Parser->GetString(L"SHA1", Sha1);
349     INT iSizeBytes;
350 
351     if (m_Parser->GetInt(L"SizeBytes", iSizeBytes))
352     {
353         SizeInBytes = (ULONG)iSizeBytes;
354     }
355     else
356     {
357         SizeInBytes = 0;
358     }
359 }
360 
361 VOID
362 CAvailableApplicationInfo::GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload)
363 {
364     License = LicenseString();
365     Size = m_szSize;
366     UrlSite = m_szUrlSite;
367     UrlDownload = m_szUrlDownload;
368 }
369 
370 InstallerType
371 CAvailableApplicationInfo::GetInstallerType() const
372 {
373     CStringW str;
374     m_Parser->GetString(DB_INSTALLER, str);
375     if (str.CompareNoCase(DB_GENINSTSECTION) == 0)
376         return INSTALLER_GENERATE;
377     else
378         return INSTALLER_UNKNOWN;
379 }
380 
381 BOOL
382 CAvailableApplicationInfo::UninstallApplication(UninstallCommandFlags Flags)
383 {
384     ATLASSERT(FALSE && "Should not be called");
385     return FALSE;
386 }
387 
388 CInstalledApplicationInfo::CInstalledApplicationInfo(
389     HKEY Key,
390     const CStringW &KeyName,
391     AppsCategories Category, UINT KeyInfo)
392     : CAppInfo(KeyName, Category), m_hKey(Key), m_KeyInfo(KeyInfo)
393 {
394     if (GetApplicationRegString(L"DisplayName", szDisplayName))
395     {
396         GetApplicationRegString(L"DisplayIcon", szDisplayIcon);
397         GetApplicationRegString(L"DisplayVersion", szDisplayVersion);
398         GetApplicationRegString(L"Comments", szComments);
399     }
400 }
401 
402 CInstalledApplicationInfo::~CInstalledApplicationInfo()
403 {
404 }
405 
406 VOID
407 CInstalledApplicationInfo::AddApplicationRegString(
408     CAppRichEdit *RichEdit,
409     UINT StringID,
410     const CStringW &String,
411     DWORD TextFlags)
412 {
413     CStringW Tmp;
414     if (GetApplicationRegString(String, Tmp))
415     {
416         RichEdit->InsertTextWithString(StringID, Tmp, TextFlags);
417     }
418 }
419 
420 VOID
421 CInstalledApplicationInfo::ShowAppInfo(CAppRichEdit *RichEdit)
422 {
423     RichEdit->SetText(szDisplayName, CFE_BOLD);
424     RichEdit->InsertText(L"\n", 0);
425 
426     RichEdit->InsertTextWithString(IDS_INFO_VERSION, szDisplayVersion, 0);
427     AddApplicationRegString(RichEdit, IDS_INFO_PUBLISHER, L"Publisher", 0);
428     AddApplicationRegString(RichEdit, IDS_INFO_REGOWNER, L"RegOwner", 0);
429     AddApplicationRegString(RichEdit, IDS_INFO_PRODUCTID, L"ProductID", 0);
430     AddApplicationRegString(RichEdit, IDS_INFO_HELPLINK, L"HelpLink", CFM_LINK);
431     AddApplicationRegString(RichEdit, IDS_INFO_HELPPHONE, L"HelpTelephone", 0);
432     AddApplicationRegString(RichEdit, IDS_INFO_README, L"Readme", 0);
433     AddApplicationRegString(RichEdit, IDS_INFO_CONTACT, L"Contact", 0);
434     AddApplicationRegString(RichEdit, IDS_INFO_UPDATEINFO, L"URLUpdateInfo", CFM_LINK);
435     AddApplicationRegString(RichEdit, IDS_INFO_INFOABOUT, L"URLInfoAbout", CFM_LINK);
436     RichEdit->InsertTextWithString(IDS_INFO_COMMENTS, szComments, 0);
437 
438     if (m_szInstallDate.IsEmpty())
439     {
440         RetrieveInstallDate();
441     }
442 
443     RichEdit->InsertTextWithString(IDS_INFO_INSTALLDATE, m_szInstallDate, 0);
444     AddApplicationRegString(RichEdit, IDS_INFO_INSTLOCATION, L"InstallLocation", 0);
445     AddApplicationRegString(RichEdit, IDS_INFO_INSTALLSRC, L"InstallSource", 0);
446 
447     if (m_szUninstallString.IsEmpty())
448     {
449         RetrieveUninstallStrings();
450     }
451 
452     RichEdit->InsertTextWithString(IDS_INFO_UNINSTALLSTR, m_szUninstallString, 0);
453     RichEdit->InsertTextWithString(IDS_INFO_MODIFYPATH, m_szModifyString, 0);
454 }
455 
456 VOID
457 CInstalledApplicationInfo::RetrieveInstallDate()
458 {
459     DWORD dwInstallTimeStamp;
460     SYSTEMTIME InstallLocalTime;
461     if (GetApplicationRegString(L"InstallDate", m_szInstallDate))
462     {
463         ZeroMemory(&InstallLocalTime, sizeof(InstallLocalTime));
464         // Check if we have 8 characters to parse the datetime.
465         // Maybe other formats exist as well?
466         m_szInstallDate = m_szInstallDate.Trim();
467         if (m_szInstallDate.GetLength() == 8)
468         {
469             InstallLocalTime.wYear = wcstol(m_szInstallDate.Left(4).GetString(), NULL, 10);
470             InstallLocalTime.wMonth = wcstol(m_szInstallDate.Mid(4, 2).GetString(), NULL, 10);
471             InstallLocalTime.wDay = wcstol(m_szInstallDate.Mid(6, 2).GetString(), NULL, 10);
472         }
473     }
474     // It might be a DWORD (Unix timestamp). try again.
475     else if (GetApplicationRegDword(L"InstallDate", &dwInstallTimeStamp))
476     {
477         FILETIME InstallFileTime;
478         SYSTEMTIME InstallSystemTime;
479 
480         UnixTimeToFileTime(dwInstallTimeStamp, &InstallFileTime);
481         FileTimeToSystemTime(&InstallFileTime, &InstallSystemTime);
482 
483         // convert to localtime
484         SystemTimeToTzSpecificLocalTime(NULL, &InstallSystemTime, &InstallLocalTime);
485     }
486 
487     // convert to readable date string
488     int cchTimeStrLen = GetDateFormatW(LOCALE_USER_DEFAULT, 0, &InstallLocalTime, NULL, 0, 0);
489 
490     GetDateFormatW(
491         LOCALE_USER_DEFAULT, // use default locale for current user
492         0, &InstallLocalTime, NULL, m_szInstallDate.GetBuffer(cchTimeStrLen), cchTimeStrLen);
493     m_szInstallDate.ReleaseBuffer();
494 }
495 
496 VOID
497 CInstalledApplicationInfo::RetrieveUninstallStrings()
498 {
499     DWORD dwWindowsInstaller = 0;
500     if (GetApplicationRegDword(L"WindowsInstaller", &dwWindowsInstaller) && dwWindowsInstaller)
501     {
502         // MSI has the same info in Uninstall / modify, so manually build it
503         m_szUninstallString.Format(L"msiexec /x%s", szIdentifier.GetString());
504     }
505     else
506     {
507         GetApplicationRegString(L"UninstallString", m_szUninstallString);
508     }
509     DWORD dwNoModify = 0;
510     if (!GetApplicationRegDword(L"NoModify", &dwNoModify))
511     {
512         CStringW Tmp;
513         if (GetApplicationRegString(L"NoModify", Tmp))
514         {
515             dwNoModify = Tmp.GetLength() > 0 ? (Tmp[0] == '1') : 0;
516         }
517         else
518         {
519             dwNoModify = 0;
520         }
521     }
522     if (!dwNoModify)
523     {
524         if (dwWindowsInstaller)
525         {
526             m_szModifyString.Format(L"msiexec /i%s", szIdentifier.GetString());
527         }
528         else
529         {
530             GetApplicationRegString(L"ModifyPath", m_szModifyString);
531         }
532     }
533 }
534 
535 BOOL
536 CInstalledApplicationInfo::Valid() const
537 {
538     return !szDisplayName.IsEmpty();
539 }
540 
541 BOOL
542 CInstalledApplicationInfo::CanModify()
543 {
544     if (m_szUninstallString.IsEmpty())
545     {
546         RetrieveUninstallStrings();
547     }
548 
549     return !m_szModifyString.IsEmpty();
550 }
551 
552 BOOL
553 CInstalledApplicationInfo::RetrieveIcon(CStringW &Path) const
554 {
555     Path = szDisplayIcon;
556     return !Path.IsEmpty();
557 }
558 
559 BOOL
560 CInstalledApplicationInfo::RetrieveScreenshot(CStringW & /*Path*/)
561 {
562     return FALSE;
563 }
564 
565 VOID
566 CInstalledApplicationInfo::GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const
567 {
568     ATLASSERT(FALSE && "Should not be called");
569 }
570 
571 VOID
572 CInstalledApplicationInfo::GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload)
573 {
574     ATLASSERT(FALSE && "Should not be called");
575 }
576 
577 InstallerType
578 CInstalledApplicationInfo::GetInstallerType() const
579 {
580     CRegKey reg;
581     if (reg.Open(m_hKey, GENERATE_ARPSUBKEY, KEY_READ) == ERROR_SUCCESS)
582     {
583         return INSTALLER_GENERATE;
584     }
585     return INSTALLER_UNKNOWN;
586 }
587 
588 BOOL
589 CInstalledApplicationInfo::UninstallApplication(UninstallCommandFlags Flags)
590 {
591     if (GetInstallerType() == INSTALLER_GENERATE)
592     {
593         return UninstallGenerated(*this, Flags);
594     }
595 
596     BOOL bModify = Flags & UCF_MODIFY;
597     if (m_szUninstallString.IsEmpty())
598     {
599         RetrieveUninstallStrings();
600     }
601 
602     CStringW cmd = bModify ? m_szModifyString : m_szUninstallString;
603     if ((Flags & (UCF_MODIFY | UCF_SILENT)) == UCF_SILENT)
604     {
605         DWORD msi = 0;
606         msi = GetApplicationRegDword(L"WindowsInstaller", &msi) && msi;
607         if (msi)
608         {
609             cmd += L" /qn";
610         }
611         else
612         {
613             CStringW silentcmd;
614             if (GetApplicationRegString(L"QuietUninstallString", silentcmd) && !silentcmd.IsEmpty())
615             {
616                 cmd = silentcmd;
617             }
618         }
619     }
620 
621     BOOL bSuccess = StartProcess(cmd, TRUE);
622 
623     if (bSuccess && !bModify)
624         WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szDisplayName);
625 
626     return bSuccess;
627 }
628 
629 BOOL
630 CInstalledApplicationInfo::GetApplicationRegString(LPCWSTR lpKeyName, CStringW &String)
631 {
632     ULONG nChars = 0;
633     // Get the size
634     if (m_hKey.QueryStringValue(lpKeyName, NULL, &nChars) != ERROR_SUCCESS)
635     {
636         String.Empty();
637         return FALSE;
638     }
639 
640     LPWSTR Buffer = String.GetBuffer(nChars);
641     LONG lResult = m_hKey.QueryStringValue(lpKeyName, Buffer, &nChars);
642     if (nChars > 0 && Buffer[nChars - 1] == UNICODE_NULL)
643         nChars--;
644     String.ReleaseBuffer(nChars);
645 
646     if (lResult != ERROR_SUCCESS)
647     {
648         String.Empty();
649         return FALSE;
650     }
651 
652     if (String.Find('%') >= 0)
653     {
654         CStringW Tmp;
655         DWORD dwLen = ExpandEnvironmentStringsW(String, NULL, 0);
656         if (dwLen > 0)
657         {
658             BOOL bSuccess = ExpandEnvironmentStringsW(String, Tmp.GetBuffer(dwLen), dwLen) == dwLen;
659             Tmp.ReleaseBuffer(dwLen - 1);
660             if (bSuccess)
661             {
662                 String = Tmp;
663             }
664             else
665             {
666                 String.Empty();
667                 return FALSE;
668             }
669         }
670     }
671 
672     return TRUE;
673 }
674 
675 BOOL
676 CInstalledApplicationInfo::GetApplicationRegDword(LPCWSTR lpKeyName, DWORD *lpValue)
677 {
678     DWORD dwSize = sizeof(DWORD), dwType;
679     if (RegQueryValueExW(m_hKey, lpKeyName, NULL, &dwType, (LPBYTE)lpValue, &dwSize) != ERROR_SUCCESS ||
680         dwType != REG_DWORD)
681     {
682         return FALSE;
683     }
684 
685     return TRUE;
686 }
687