14d0cc206SHe Yang /*
24d0cc206SHe Yang * PROJECT: ReactOS system libraries
34d0cc206SHe Yang * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
44d0cc206SHe Yang * PURPOSE: Functions to read RunOnceEx registry.
54d0cc206SHe Yang * COPYRIGHT: Copyright 2021 He Yang <1160386205@qq.com>
64d0cc206SHe Yang */
74d0cc206SHe Yang
84d0cc206SHe Yang #include "iernonce.h"
94d0cc206SHe Yang
104d0cc206SHe Yang extern RUNONCEEX_CALLBACK g_Callback;
114d0cc206SHe Yang
EnumValueName(_In_ DWORD iIndex,_Out_ LPTSTR pszName,_Inout_ LPDWORD pnNameLength)124d0cc206SHe Yang LONG CRegKeyEx::EnumValueName(
134d0cc206SHe Yang _In_ DWORD iIndex,
144d0cc206SHe Yang _Out_ LPTSTR pszName,
154d0cc206SHe Yang _Inout_ LPDWORD pnNameLength)
164d0cc206SHe Yang {
174d0cc206SHe Yang return RegEnumValueW(m_hKey, iIndex, pszName, pnNameLength,
184d0cc206SHe Yang NULL, NULL, NULL, NULL);
194d0cc206SHe Yang }
204d0cc206SHe Yang
RunOnceExEntry(_In_ const ATL::CStringW & Name,_In_ const ATL::CStringW & Value)214d0cc206SHe Yang RunOnceExEntry::RunOnceExEntry(
224d0cc206SHe Yang _In_ const ATL::CStringW &Name,
234d0cc206SHe Yang _In_ const ATL::CStringW &Value) :
244d0cc206SHe Yang m_Value(Value), m_Name(Name)
254d0cc206SHe Yang { ; }
264d0cc206SHe Yang
Delete(_In_ CRegKeyEx & hParentKey)274d0cc206SHe Yang BOOL RunOnceExEntry::Delete(
284d0cc206SHe Yang _In_ CRegKeyEx &hParentKey)
294d0cc206SHe Yang {
304d0cc206SHe Yang return hParentKey.DeleteValue(m_Name) == ERROR_SUCCESS;
314d0cc206SHe Yang }
324d0cc206SHe Yang
Exec() const334d0cc206SHe Yang BOOL RunOnceExEntry::Exec() const
344d0cc206SHe Yang {
354d0cc206SHe Yang CStringW CommandLine;
364d0cc206SHe Yang if (wcsncmp(m_Value, L"||", 2) == 0)
374d0cc206SHe Yang {
384d0cc206SHe Yang // Remove the prefix.
394d0cc206SHe Yang CommandLine = (LPCWSTR)m_Value + 2;
404d0cc206SHe Yang }
414d0cc206SHe Yang else
424d0cc206SHe Yang {
434d0cc206SHe Yang CommandLine = m_Value;
444d0cc206SHe Yang }
454d0cc206SHe Yang
464d0cc206SHe Yang // FIXME: SHEvaluateSystemCommandTemplate is not implemented
474d0cc206SHe Yang // using PathGetArgsW, PathRemoveArgsW as a workaround.
484d0cc206SHe Yang LPWSTR szCommandLine = CommandLine.GetBuffer();
494d0cc206SHe Yang LPCWSTR szParam = PathGetArgsW(szCommandLine);
504d0cc206SHe Yang PathRemoveArgsW(szCommandLine);
514d0cc206SHe Yang
524d0cc206SHe Yang SHELLEXECUTEINFOW Info = { 0 };
534d0cc206SHe Yang Info.cbSize = sizeof(Info);
544d0cc206SHe Yang Info.fMask = SEE_MASK_NOCLOSEPROCESS;
554d0cc206SHe Yang Info.lpFile = szCommandLine;
564d0cc206SHe Yang Info.lpParameters = szParam;
574d0cc206SHe Yang Info.nShow = SW_SHOWNORMAL;
584d0cc206SHe Yang
594d0cc206SHe Yang BOOL bSuccess = ShellExecuteExW(&Info);
604d0cc206SHe Yang
614d0cc206SHe Yang CommandLine.ReleaseBuffer();
624d0cc206SHe Yang
634d0cc206SHe Yang if (!bSuccess)
644d0cc206SHe Yang {
654d0cc206SHe Yang return FALSE;
664d0cc206SHe Yang }
674d0cc206SHe Yang
684d0cc206SHe Yang if (Info.hProcess)
694d0cc206SHe Yang {
704d0cc206SHe Yang WaitForSingleObject(Info.hProcess, INFINITE);
714d0cc206SHe Yang CloseHandle(Info.hProcess);
724d0cc206SHe Yang }
734d0cc206SHe Yang
744d0cc206SHe Yang return TRUE;
754d0cc206SHe Yang }
764d0cc206SHe Yang
RunOnceExEntryCmp(_In_ const void * a,_In_ const void * b)774d0cc206SHe Yang int RunOnceExEntryCmp(
784d0cc206SHe Yang _In_ const void *a,
794d0cc206SHe Yang _In_ const void *b)
804d0cc206SHe Yang {
814d0cc206SHe Yang return lstrcmpW(((RunOnceExEntry *)a)->m_Name,
824d0cc206SHe Yang ((RunOnceExEntry *)b)->m_Name);
834d0cc206SHe Yang }
844d0cc206SHe Yang
HandleValue(_In_ CRegKeyEx & hKey,_In_ const CStringW & ValueName)854d0cc206SHe Yang BOOL RunOnceExSection::HandleValue(
864d0cc206SHe Yang _In_ CRegKeyEx &hKey,
874d0cc206SHe Yang _In_ const CStringW &ValueName)
884d0cc206SHe Yang {
894d0cc206SHe Yang DWORD dwType;
904d0cc206SHe Yang DWORD cbData;
914d0cc206SHe Yang
924d0cc206SHe Yang // Query data size
934d0cc206SHe Yang if (hKey.QueryValue(ValueName, &dwType, NULL, &cbData) != ERROR_SUCCESS)
944d0cc206SHe Yang return FALSE;
954d0cc206SHe Yang
964d0cc206SHe Yang // Validate its format and size.
974d0cc206SHe Yang if (dwType != REG_SZ)
984d0cc206SHe Yang return TRUE;
994d0cc206SHe Yang
1004d0cc206SHe Yang if (cbData % sizeof(WCHAR) != 0)
1014d0cc206SHe Yang return FALSE;
1024d0cc206SHe Yang
1034d0cc206SHe Yang CStringW Buffer;
1044d0cc206SHe Yang LPWSTR szBuffer = Buffer.GetBuffer((cbData / sizeof(WCHAR)) + 1);
1054d0cc206SHe Yang
1064d0cc206SHe Yang if (hKey.QueryValue(ValueName, &dwType, szBuffer, &cbData) != ERROR_SUCCESS)
1074d0cc206SHe Yang {
1084d0cc206SHe Yang Buffer.ReleaseBuffer();
1094d0cc206SHe Yang return FALSE;
1104d0cc206SHe Yang }
1114d0cc206SHe Yang szBuffer[cbData / sizeof(WCHAR)] = L'\0';
1124d0cc206SHe Yang Buffer.ReleaseBuffer();
1134d0cc206SHe Yang
1144d0cc206SHe Yang CStringW ExpandStr;
1154d0cc206SHe Yang DWORD dwcchExpand = ExpandEnvironmentStringsW(Buffer, NULL, 0);
1164d0cc206SHe Yang ExpandEnvironmentStringsW(Buffer, ExpandStr.GetBuffer(dwcchExpand + 1), dwcchExpand);
1174d0cc206SHe Yang ExpandStr.ReleaseBuffer();
1184d0cc206SHe Yang
1194d0cc206SHe Yang if (ValueName.IsEmpty())
1204d0cc206SHe Yang {
1214d0cc206SHe Yang // The default value specifies the section title.
1224d0cc206SHe Yang m_SectionTitle = Buffer;
1234d0cc206SHe Yang }
1244d0cc206SHe Yang else
1254d0cc206SHe Yang {
1264d0cc206SHe Yang m_EntryList.Add(RunOnceExEntry(ValueName, ExpandStr));
1274d0cc206SHe Yang }
1284d0cc206SHe Yang
1294d0cc206SHe Yang return TRUE;
1304d0cc206SHe Yang }
1314d0cc206SHe Yang
RunOnceExSection(_In_ CRegKeyEx & hParentKey,_In_ const CStringW & lpSubKeyName)1324d0cc206SHe Yang RunOnceExSection::RunOnceExSection(
1334d0cc206SHe Yang _In_ CRegKeyEx &hParentKey,
1344d0cc206SHe Yang _In_ const CStringW &lpSubKeyName) :
1354d0cc206SHe Yang m_SectionName(lpSubKeyName)
1364d0cc206SHe Yang {
1374d0cc206SHe Yang m_bSuccess = FALSE;
1384d0cc206SHe Yang DWORD dwValueNum;
1394d0cc206SHe Yang DWORD dwMaxValueNameLen;
1404d0cc206SHe Yang LSTATUS Error;
1414d0cc206SHe Yang CStringW ValueName;
1424d0cc206SHe Yang
1434d0cc206SHe Yang if (m_RegKey.Open(hParentKey, lpSubKeyName) != ERROR_SUCCESS)
1444d0cc206SHe Yang return;
1454d0cc206SHe Yang
1464d0cc206SHe Yang Error = RegQueryInfoKeyW(m_RegKey, NULL, 0, NULL, NULL, NULL, NULL,
1474d0cc206SHe Yang &dwValueNum, &dwMaxValueNameLen,
1484d0cc206SHe Yang NULL, NULL, NULL);
1494d0cc206SHe Yang if (Error != ERROR_SUCCESS)
1504d0cc206SHe Yang return;
1514d0cc206SHe Yang
1524d0cc206SHe Yang for (DWORD i = 0; i < dwValueNum; i++)
1534d0cc206SHe Yang {
1544d0cc206SHe Yang LPWSTR szValueName;
1554d0cc206SHe Yang DWORD dwcchName = dwMaxValueNameLen + 1;
1564d0cc206SHe Yang
1574d0cc206SHe Yang szValueName = ValueName.GetBuffer(dwMaxValueNameLen + 1);
1584d0cc206SHe Yang Error = m_RegKey.EnumValueName(i, szValueName, &dwcchName);
1594d0cc206SHe Yang ValueName.ReleaseBuffer();
1604d0cc206SHe Yang
1614d0cc206SHe Yang if (Error != ERROR_SUCCESS)
1624d0cc206SHe Yang {
1634d0cc206SHe Yang // TODO: error handling
1644d0cc206SHe Yang return;
1654d0cc206SHe Yang }
1664d0cc206SHe Yang
1674d0cc206SHe Yang if (!HandleValue(m_RegKey, ValueName))
1684d0cc206SHe Yang return;
1694d0cc206SHe Yang }
1704d0cc206SHe Yang
1714d0cc206SHe Yang // Sort entries by name in string order.
1724d0cc206SHe Yang qsort(m_EntryList.GetData(), m_EntryList.GetSize(),
1734d0cc206SHe Yang sizeof(RunOnceExEntry), RunOnceExEntryCmp);
1744d0cc206SHe Yang
1754d0cc206SHe Yang m_bSuccess = TRUE;
1764d0cc206SHe Yang return;
1774d0cc206SHe Yang }
1784d0cc206SHe Yang
1794d0cc206SHe Yang // Copy constructor, CSimpleArray needs it.
RunOnceExSection(_In_ const RunOnceExSection & Section)1804d0cc206SHe Yang RunOnceExSection::RunOnceExSection(_In_ const RunOnceExSection& Section) :
1814d0cc206SHe Yang m_SectionName(Section.m_SectionName),
1824d0cc206SHe Yang m_bSuccess(Section.m_bSuccess),
1834d0cc206SHe Yang m_SectionTitle(Section.m_SectionTitle),
1844d0cc206SHe Yang m_EntryList(Section.m_EntryList)
1854d0cc206SHe Yang {
1864d0cc206SHe Yang m_RegKey.Attach(Section.m_RegKey);
1874d0cc206SHe Yang }
1884d0cc206SHe Yang
CloseAndDelete(_In_ CRegKeyEx & hParentKey)1894d0cc206SHe Yang BOOL RunOnceExSection::CloseAndDelete(
1904d0cc206SHe Yang _In_ CRegKeyEx &hParentKey)
1914d0cc206SHe Yang {
1924d0cc206SHe Yang m_RegKey.Close();
1934d0cc206SHe Yang return hParentKey.RecurseDeleteKey(m_SectionName) == ERROR_SUCCESS;
1944d0cc206SHe Yang }
1954d0cc206SHe Yang
GetEntryCnt() const1964d0cc206SHe Yang UINT RunOnceExSection::GetEntryCnt() const
1974d0cc206SHe Yang {
1984d0cc206SHe Yang return m_EntryList.GetSize();
1994d0cc206SHe Yang }
2004d0cc206SHe Yang
Exec(_Inout_ UINT & iCompleteCnt,_In_ const UINT iTotalCnt)2014d0cc206SHe Yang BOOL RunOnceExSection::Exec(
2024d0cc206SHe Yang _Inout_ UINT& iCompleteCnt,
2034d0cc206SHe Yang _In_ const UINT iTotalCnt)
2044d0cc206SHe Yang {
2054d0cc206SHe Yang BOOL bSuccess = TRUE;
2064d0cc206SHe Yang
2074d0cc206SHe Yang for (int i = 0; i < m_EntryList.GetSize(); i++)
2084d0cc206SHe Yang {
2094d0cc206SHe Yang m_EntryList[i].Delete(m_RegKey);
2104d0cc206SHe Yang bSuccess &= m_EntryList[i].Exec();
2114d0cc206SHe Yang iCompleteCnt++;
2124d0cc206SHe Yang // TODO: the meaning of the third param is still unknown, seems it's always 0.
2134d0cc206SHe Yang if (g_Callback)
2144d0cc206SHe Yang g_Callback(iCompleteCnt, iTotalCnt, NULL);
2154d0cc206SHe Yang }
2164d0cc206SHe Yang return bSuccess;
2174d0cc206SHe Yang }
2184d0cc206SHe Yang
RunOnceExSectionCmp(_In_ const void * a,_In_ const void * b)2194d0cc206SHe Yang int RunOnceExSectionCmp(
2204d0cc206SHe Yang _In_ const void *a,
2214d0cc206SHe Yang _In_ const void *b)
2224d0cc206SHe Yang {
2234d0cc206SHe Yang return lstrcmpW(((RunOnceExSection *)a)->m_SectionName,
2244d0cc206SHe Yang ((RunOnceExSection *)b)->m_SectionName);
2254d0cc206SHe Yang }
2264d0cc206SHe Yang
RunOnceExInstance(_In_ HKEY BaseKey)2274d0cc206SHe Yang RunOnceExInstance::RunOnceExInstance(_In_ HKEY BaseKey)
2284d0cc206SHe Yang {
2294d0cc206SHe Yang m_bSuccess = FALSE;
2304d0cc206SHe Yang DWORD dwSubKeyNum;
2314d0cc206SHe Yang DWORD dwMaxSubKeyNameLen;
2324d0cc206SHe Yang LSTATUS Error;
2334d0cc206SHe Yang CStringW SubKeyName;
2344d0cc206SHe Yang
2354d0cc206SHe Yang Error = m_RegKey.Open(BaseKey,
2364d0cc206SHe Yang L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx\\");
2374d0cc206SHe Yang if (Error != ERROR_SUCCESS)
2384d0cc206SHe Yang {
2394d0cc206SHe Yang return;
2404d0cc206SHe Yang }
2414d0cc206SHe Yang
2424d0cc206SHe Yang ULONG cchTitle;
2434d0cc206SHe Yang Error = m_RegKey.QueryStringValue(L"Title", NULL, &cchTitle);
2444d0cc206SHe Yang if (Error == ERROR_SUCCESS)
2454d0cc206SHe Yang {
2464d0cc206SHe Yang Error = m_RegKey.QueryStringValue(L"Title", m_Title.GetBuffer(cchTitle + 1), &cchTitle);
2474d0cc206SHe Yang m_Title.ReleaseBuffer();
2484d0cc206SHe Yang if (Error != ERROR_SUCCESS)
2494d0cc206SHe Yang return;
2504d0cc206SHe Yang }
2514d0cc206SHe Yang
2524d0cc206SHe Yang Error = m_RegKey.QueryDWORDValue(L"Flags", m_dwFlags);
2534d0cc206SHe Yang if (Error != ERROR_SUCCESS)
2544d0cc206SHe Yang {
2554d0cc206SHe Yang m_dwFlags = 0;
2564d0cc206SHe Yang }
2574d0cc206SHe Yang
2584d0cc206SHe Yang Error = RegQueryInfoKeyW(m_RegKey, NULL, 0, NULL,
2594d0cc206SHe Yang &dwSubKeyNum, &dwMaxSubKeyNameLen,
2604d0cc206SHe Yang NULL, NULL, NULL, NULL, NULL, NULL);
2614d0cc206SHe Yang if (Error != ERROR_SUCCESS)
2624d0cc206SHe Yang return;
2634d0cc206SHe Yang
2644d0cc206SHe Yang m_bShowDialog = FALSE;
2654d0cc206SHe Yang
2664d0cc206SHe Yang for (DWORD i = 0; i < dwSubKeyNum; i++)
2674d0cc206SHe Yang {
2684d0cc206SHe Yang LPWSTR szSubKeyName;
2694d0cc206SHe Yang DWORD dwcchName = dwMaxSubKeyNameLen + 1;
2704d0cc206SHe Yang
2714d0cc206SHe Yang szSubKeyName = SubKeyName.GetBuffer(dwMaxSubKeyNameLen + 1);
2724d0cc206SHe Yang Error = m_RegKey.EnumKey(i, szSubKeyName, &dwcchName);
2734d0cc206SHe Yang SubKeyName.ReleaseBuffer();
2744d0cc206SHe Yang
2754d0cc206SHe Yang if (Error != ERROR_SUCCESS)
2764d0cc206SHe Yang {
2774d0cc206SHe Yang // TODO: error handling
2784d0cc206SHe Yang return;
2794d0cc206SHe Yang }
2804d0cc206SHe Yang
2814d0cc206SHe Yang if (!HandleSubKey(m_RegKey, SubKeyName))
2824d0cc206SHe Yang return;
2834d0cc206SHe Yang }
2844d0cc206SHe Yang
2854d0cc206SHe Yang // Sort sections by name in string order.
2864d0cc206SHe Yang qsort(m_SectionList.GetData(), m_SectionList.GetSize(),
2874d0cc206SHe Yang sizeof(RunOnceExSection), RunOnceExSectionCmp);
2884d0cc206SHe Yang
2894d0cc206SHe Yang m_bSuccess = TRUE;
2904d0cc206SHe Yang return;
2914d0cc206SHe Yang }
2924d0cc206SHe Yang
Exec(_In_opt_ HWND hwnd)2934d0cc206SHe Yang BOOL RunOnceExInstance::Exec(_In_opt_ HWND hwnd)
2944d0cc206SHe Yang {
2954d0cc206SHe Yang BOOL bSuccess = TRUE;
2964d0cc206SHe Yang
2974d0cc206SHe Yang UINT TotalCnt = 0;
2984d0cc206SHe Yang UINT CompleteCnt = 0;
2994d0cc206SHe Yang for (int i = 0; i < m_SectionList.GetSize(); i++)
3004d0cc206SHe Yang {
3014d0cc206SHe Yang TotalCnt += m_SectionList[i].GetEntryCnt();
3024d0cc206SHe Yang }
3034d0cc206SHe Yang
3044d0cc206SHe Yang // Execute items from registry one by one, and remove them.
3054d0cc206SHe Yang for (int i = 0; i < m_SectionList.GetSize(); i++)
3064d0cc206SHe Yang {
3074d0cc206SHe Yang if (hwnd)
3084d0cc206SHe Yang SendMessageW(hwnd, WM_SETINDEX, i, 0);
3094d0cc206SHe Yang
3104d0cc206SHe Yang bSuccess &= m_SectionList[i].Exec(CompleteCnt, TotalCnt);
3114d0cc206SHe Yang m_SectionList[i].CloseAndDelete(m_RegKey);
3124d0cc206SHe Yang }
3134d0cc206SHe Yang
314*40b45515STimo Kreuzer if (m_RegKey)
315*40b45515STimo Kreuzer {
3164d0cc206SHe Yang m_RegKey.DeleteValue(L"Title");
3174d0cc206SHe Yang m_RegKey.DeleteValue(L"Flags");
318*40b45515STimo Kreuzer }
3194d0cc206SHe Yang
3204d0cc206SHe Yang // Notify the dialog all sections are handled.
3214d0cc206SHe Yang if (hwnd)
3224d0cc206SHe Yang SendMessageW(hwnd, WM_SETINDEX, m_SectionList.GetSize(), bSuccess);
3234d0cc206SHe Yang return bSuccess;
3244d0cc206SHe Yang }
3254d0cc206SHe Yang
Run(_In_ BOOL bSilence)3264d0cc206SHe Yang BOOL RunOnceExInstance::Run(_In_ BOOL bSilence)
3274d0cc206SHe Yang {
3284d0cc206SHe Yang if (bSilence ||
3294d0cc206SHe Yang (m_dwFlags & FLAGS_NO_STAT_DIALOG) ||
3304d0cc206SHe Yang !m_bShowDialog)
3314d0cc206SHe Yang {
3324d0cc206SHe Yang return Exec(NULL);
3334d0cc206SHe Yang }
3344d0cc206SHe Yang else
3354d0cc206SHe Yang {
3364d0cc206SHe Yang // The dialog is responsible to create a thread and execute.
3374d0cc206SHe Yang ProgressDlg dlg(*this);
3384d0cc206SHe Yang return dlg.RunDialogBox();
3394d0cc206SHe Yang }
3404d0cc206SHe Yang }
3414d0cc206SHe Yang
HandleSubKey(_In_ CRegKeyEx & hKey,_In_ const CStringW & SubKeyName)3424d0cc206SHe Yang BOOL RunOnceExInstance::HandleSubKey(
3434d0cc206SHe Yang _In_ CRegKeyEx &hKey,
3444d0cc206SHe Yang _In_ const CStringW& SubKeyName)
3454d0cc206SHe Yang {
3464d0cc206SHe Yang RunOnceExSection Section(hKey, SubKeyName);
3474d0cc206SHe Yang if (!Section.m_bSuccess)
3484d0cc206SHe Yang {
3494d0cc206SHe Yang return FALSE;
3504d0cc206SHe Yang }
3514d0cc206SHe Yang
3524d0cc206SHe Yang if (!Section.m_SectionTitle.IsEmpty())
3534d0cc206SHe Yang {
3544d0cc206SHe Yang m_bShowDialog = TRUE;
3554d0cc206SHe Yang }
3564d0cc206SHe Yang m_SectionList.Add(Section);
3574d0cc206SHe Yang
3584d0cc206SHe Yang // The copy constructor of RunOnceExSection didn't detach
3594d0cc206SHe Yang // the m_RegKey while it's attached to the one in the array.
3604d0cc206SHe Yang // So we have to detach it manually.
3614d0cc206SHe Yang Section.m_RegKey.Detach();
3624d0cc206SHe Yang return TRUE;
3634d0cc206SHe Yang }
364