xref: /reactos/dll/win32/iernonce/registry.cpp (revision 40b45515)
1 /*
2  * PROJECT:     ReactOS system libraries
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Functions to read RunOnceEx registry.
5  * COPYRIGHT:   Copyright 2021 He Yang <1160386205@qq.com>
6  */
7 
8 #include "iernonce.h"
9 
10 extern RUNONCEEX_CALLBACK g_Callback;
11 
EnumValueName(_In_ DWORD iIndex,_Out_ LPTSTR pszName,_Inout_ LPDWORD pnNameLength)12 LONG CRegKeyEx::EnumValueName(
13     _In_ DWORD iIndex,
14     _Out_ LPTSTR pszName,
15     _Inout_ LPDWORD pnNameLength)
16 {
17     return RegEnumValueW(m_hKey, iIndex, pszName, pnNameLength,
18                          NULL, NULL, NULL, NULL);
19 }
20 
RunOnceExEntry(_In_ const ATL::CStringW & Name,_In_ const ATL::CStringW & Value)21 RunOnceExEntry::RunOnceExEntry(
22     _In_ const ATL::CStringW &Name,
23     _In_ const ATL::CStringW &Value) :
24     m_Value(Value), m_Name(Name)
25 { ; }
26 
Delete(_In_ CRegKeyEx & hParentKey)27 BOOL RunOnceExEntry::Delete(
28     _In_ CRegKeyEx &hParentKey)
29 {
30     return hParentKey.DeleteValue(m_Name) == ERROR_SUCCESS;
31 }
32 
Exec() const33 BOOL RunOnceExEntry::Exec() const
34 {
35     CStringW CommandLine;
36     if (wcsncmp(m_Value, L"||", 2) == 0)
37     {
38         // Remove the prefix.
39         CommandLine = (LPCWSTR)m_Value + 2;
40     }
41     else
42     {
43         CommandLine = m_Value;
44     }
45 
46     // FIXME: SHEvaluateSystemCommandTemplate is not implemented
47     //        using PathGetArgsW, PathRemoveArgsW as a workaround.
48     LPWSTR szCommandLine = CommandLine.GetBuffer();
49     LPCWSTR szParam = PathGetArgsW(szCommandLine);
50     PathRemoveArgsW(szCommandLine);
51 
52     SHELLEXECUTEINFOW Info = { 0 };
53     Info.cbSize = sizeof(Info);
54     Info.fMask = SEE_MASK_NOCLOSEPROCESS;
55     Info.lpFile = szCommandLine;
56     Info.lpParameters = szParam;
57     Info.nShow = SW_SHOWNORMAL;
58 
59     BOOL bSuccess = ShellExecuteExW(&Info);
60 
61     CommandLine.ReleaseBuffer();
62 
63     if (!bSuccess)
64     {
65         return FALSE;
66     }
67 
68     if (Info.hProcess)
69     {
70         WaitForSingleObject(Info.hProcess, INFINITE);
71         CloseHandle(Info.hProcess);
72     }
73 
74     return TRUE;
75 }
76 
RunOnceExEntryCmp(_In_ const void * a,_In_ const void * b)77 int RunOnceExEntryCmp(
78     _In_ const void *a,
79     _In_ const void *b)
80 {
81     return lstrcmpW(((RunOnceExEntry *)a)->m_Name,
82                     ((RunOnceExEntry *)b)->m_Name);
83 }
84 
HandleValue(_In_ CRegKeyEx & hKey,_In_ const CStringW & ValueName)85 BOOL RunOnceExSection::HandleValue(
86     _In_ CRegKeyEx &hKey,
87     _In_ const CStringW &ValueName)
88 {
89     DWORD dwType;
90     DWORD cbData;
91 
92     // Query data size
93     if (hKey.QueryValue(ValueName, &dwType, NULL, &cbData) != ERROR_SUCCESS)
94         return FALSE;
95 
96     // Validate its format and size.
97     if (dwType != REG_SZ)
98         return TRUE;
99 
100     if (cbData % sizeof(WCHAR) != 0)
101         return FALSE;
102 
103     CStringW Buffer;
104     LPWSTR szBuffer = Buffer.GetBuffer((cbData / sizeof(WCHAR)) + 1);
105 
106     if (hKey.QueryValue(ValueName, &dwType, szBuffer, &cbData) != ERROR_SUCCESS)
107     {
108         Buffer.ReleaseBuffer();
109         return FALSE;
110     }
111     szBuffer[cbData / sizeof(WCHAR)] = L'\0';
112     Buffer.ReleaseBuffer();
113 
114     CStringW ExpandStr;
115     DWORD dwcchExpand = ExpandEnvironmentStringsW(Buffer, NULL, 0);
116     ExpandEnvironmentStringsW(Buffer, ExpandStr.GetBuffer(dwcchExpand + 1), dwcchExpand);
117     ExpandStr.ReleaseBuffer();
118 
119     if (ValueName.IsEmpty())
120     {
121         // The default value specifies the section title.
122         m_SectionTitle = Buffer;
123     }
124     else
125     {
126         m_EntryList.Add(RunOnceExEntry(ValueName, ExpandStr));
127     }
128 
129     return TRUE;
130 }
131 
RunOnceExSection(_In_ CRegKeyEx & hParentKey,_In_ const CStringW & lpSubKeyName)132 RunOnceExSection::RunOnceExSection(
133     _In_ CRegKeyEx &hParentKey,
134     _In_ const CStringW &lpSubKeyName) :
135     m_SectionName(lpSubKeyName)
136 {
137     m_bSuccess = FALSE;
138     DWORD dwValueNum;
139     DWORD dwMaxValueNameLen;
140     LSTATUS Error;
141     CStringW ValueName;
142 
143     if (m_RegKey.Open(hParentKey, lpSubKeyName) != ERROR_SUCCESS)
144         return;
145 
146     Error = RegQueryInfoKeyW(m_RegKey, NULL, 0, NULL, NULL, NULL, NULL,
147                              &dwValueNum, &dwMaxValueNameLen,
148                              NULL, NULL, NULL);
149     if (Error != ERROR_SUCCESS)
150         return;
151 
152     for (DWORD i = 0; i < dwValueNum; i++)
153     {
154         LPWSTR szValueName;
155         DWORD dwcchName = dwMaxValueNameLen + 1;
156 
157         szValueName = ValueName.GetBuffer(dwMaxValueNameLen + 1);
158         Error = m_RegKey.EnumValueName(i, szValueName, &dwcchName);
159         ValueName.ReleaseBuffer();
160 
161         if (Error != ERROR_SUCCESS)
162         {
163             // TODO: error handling
164             return;
165         }
166 
167         if (!HandleValue(m_RegKey, ValueName))
168             return;
169     }
170 
171     // Sort entries by name in string order.
172     qsort(m_EntryList.GetData(), m_EntryList.GetSize(),
173           sizeof(RunOnceExEntry), RunOnceExEntryCmp);
174 
175     m_bSuccess = TRUE;
176     return;
177 }
178 
179 // Copy constructor, CSimpleArray needs it.
RunOnceExSection(_In_ const RunOnceExSection & Section)180 RunOnceExSection::RunOnceExSection(_In_ const RunOnceExSection& Section) :
181     m_SectionName(Section.m_SectionName),
182     m_bSuccess(Section.m_bSuccess),
183     m_SectionTitle(Section.m_SectionTitle),
184     m_EntryList(Section.m_EntryList)
185 {
186     m_RegKey.Attach(Section.m_RegKey);
187 }
188 
CloseAndDelete(_In_ CRegKeyEx & hParentKey)189 BOOL RunOnceExSection::CloseAndDelete(
190     _In_ CRegKeyEx &hParentKey)
191 {
192     m_RegKey.Close();
193     return hParentKey.RecurseDeleteKey(m_SectionName) == ERROR_SUCCESS;
194 }
195 
GetEntryCnt() const196 UINT RunOnceExSection::GetEntryCnt() const
197 {
198     return m_EntryList.GetSize();
199 }
200 
Exec(_Inout_ UINT & iCompleteCnt,_In_ const UINT iTotalCnt)201 BOOL RunOnceExSection::Exec(
202     _Inout_ UINT& iCompleteCnt,
203     _In_ const UINT iTotalCnt)
204 {
205     BOOL bSuccess = TRUE;
206 
207     for (int i = 0; i < m_EntryList.GetSize(); i++)
208     {
209         m_EntryList[i].Delete(m_RegKey);
210         bSuccess &= m_EntryList[i].Exec();
211         iCompleteCnt++;
212         // TODO: the meaning of the third param is still unknown, seems it's always 0.
213         if (g_Callback)
214             g_Callback(iCompleteCnt, iTotalCnt, NULL);
215     }
216     return bSuccess;
217 }
218 
RunOnceExSectionCmp(_In_ const void * a,_In_ const void * b)219 int RunOnceExSectionCmp(
220     _In_ const void *a,
221     _In_ const void *b)
222 {
223     return lstrcmpW(((RunOnceExSection *)a)->m_SectionName,
224                     ((RunOnceExSection *)b)->m_SectionName);
225 }
226 
RunOnceExInstance(_In_ HKEY BaseKey)227 RunOnceExInstance::RunOnceExInstance(_In_ HKEY BaseKey)
228 {
229     m_bSuccess = FALSE;
230     DWORD dwSubKeyNum;
231     DWORD dwMaxSubKeyNameLen;
232     LSTATUS Error;
233     CStringW SubKeyName;
234 
235     Error = m_RegKey.Open(BaseKey,
236                           L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx\\");
237     if (Error != ERROR_SUCCESS)
238     {
239         return;
240     }
241 
242     ULONG cchTitle;
243     Error = m_RegKey.QueryStringValue(L"Title", NULL, &cchTitle);
244     if (Error == ERROR_SUCCESS)
245     {
246         Error = m_RegKey.QueryStringValue(L"Title", m_Title.GetBuffer(cchTitle + 1), &cchTitle);
247         m_Title.ReleaseBuffer();
248         if (Error != ERROR_SUCCESS)
249             return;
250     }
251 
252     Error = m_RegKey.QueryDWORDValue(L"Flags", m_dwFlags);
253     if (Error != ERROR_SUCCESS)
254     {
255         m_dwFlags = 0;
256     }
257 
258     Error = RegQueryInfoKeyW(m_RegKey, NULL, 0, NULL,
259                              &dwSubKeyNum, &dwMaxSubKeyNameLen,
260                              NULL, NULL, NULL, NULL, NULL, NULL);
261     if (Error != ERROR_SUCCESS)
262         return;
263 
264     m_bShowDialog = FALSE;
265 
266     for (DWORD i = 0; i < dwSubKeyNum; i++)
267     {
268         LPWSTR szSubKeyName;
269         DWORD dwcchName = dwMaxSubKeyNameLen + 1;
270 
271         szSubKeyName = SubKeyName.GetBuffer(dwMaxSubKeyNameLen + 1);
272         Error = m_RegKey.EnumKey(i, szSubKeyName, &dwcchName);
273         SubKeyName.ReleaseBuffer();
274 
275         if (Error != ERROR_SUCCESS)
276         {
277             // TODO: error handling
278             return;
279         }
280 
281         if (!HandleSubKey(m_RegKey, SubKeyName))
282             return;
283     }
284 
285     // Sort sections by name in string order.
286     qsort(m_SectionList.GetData(), m_SectionList.GetSize(),
287           sizeof(RunOnceExSection), RunOnceExSectionCmp);
288 
289     m_bSuccess = TRUE;
290     return;
291 }
292 
Exec(_In_opt_ HWND hwnd)293 BOOL RunOnceExInstance::Exec(_In_opt_ HWND hwnd)
294 {
295     BOOL bSuccess = TRUE;
296 
297     UINT TotalCnt = 0;
298     UINT CompleteCnt = 0;
299     for (int i = 0; i < m_SectionList.GetSize(); i++)
300     {
301         TotalCnt += m_SectionList[i].GetEntryCnt();
302     }
303 
304     // Execute items from registry one by one, and remove them.
305     for (int i = 0; i < m_SectionList.GetSize(); i++)
306     {
307         if (hwnd)
308             SendMessageW(hwnd, WM_SETINDEX, i, 0);
309 
310         bSuccess &= m_SectionList[i].Exec(CompleteCnt, TotalCnt);
311         m_SectionList[i].CloseAndDelete(m_RegKey);
312     }
313 
314     if (m_RegKey)
315     {
316         m_RegKey.DeleteValue(L"Title");
317         m_RegKey.DeleteValue(L"Flags");
318     }
319 
320     // Notify the dialog all sections are handled.
321     if (hwnd)
322         SendMessageW(hwnd, WM_SETINDEX, m_SectionList.GetSize(), bSuccess);
323     return bSuccess;
324 }
325 
Run(_In_ BOOL bSilence)326 BOOL RunOnceExInstance::Run(_In_ BOOL bSilence)
327 {
328     if (bSilence ||
329         (m_dwFlags & FLAGS_NO_STAT_DIALOG) ||
330         !m_bShowDialog)
331     {
332         return Exec(NULL);
333     }
334     else
335     {
336         // The dialog is responsible to create a thread and execute.
337         ProgressDlg dlg(*this);
338         return dlg.RunDialogBox();
339     }
340 }
341 
HandleSubKey(_In_ CRegKeyEx & hKey,_In_ const CStringW & SubKeyName)342 BOOL RunOnceExInstance::HandleSubKey(
343     _In_ CRegKeyEx &hKey,
344     _In_ const CStringW& SubKeyName)
345 {
346     RunOnceExSection Section(hKey, SubKeyName);
347     if (!Section.m_bSuccess)
348     {
349         return FALSE;
350     }
351 
352     if (!Section.m_SectionTitle.IsEmpty())
353     {
354         m_bShowDialog = TRUE;
355     }
356     m_SectionList.Add(Section);
357 
358     // The copy constructor of RunOnceExSection didn't detach
359     // the m_RegKey while it's attached to the one in the array.
360     // So we have to detach it manually.
361     Section.m_RegKey.Detach();
362     return TRUE;
363 }
364