xref: /reactos/dll/win32/iernonce/registry.cpp (revision 40b45515)
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