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 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 21 RunOnceExEntry::RunOnceExEntry( 22 _In_ const ATL::CStringW &Name, 23 _In_ const ATL::CStringW &Value) : 24 m_Value(Value), m_Name(Name) 25 { ; } 26 27 BOOL RunOnceExEntry::Delete( 28 _In_ CRegKeyEx &hParentKey) 29 { 30 return hParentKey.DeleteValue(m_Name) == ERROR_SUCCESS; 31 } 32 33 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 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 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 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. 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 189 BOOL RunOnceExSection::CloseAndDelete( 190 _In_ CRegKeyEx &hParentKey) 191 { 192 m_RegKey.Close(); 193 return hParentKey.RecurseDeleteKey(m_SectionName) == ERROR_SUCCESS; 194 } 195 196 UINT RunOnceExSection::GetEntryCnt() const 197 { 198 return m_EntryList.GetSize(); 199 } 200 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 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 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 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 m_RegKey.DeleteValue(L"Title"); 315 m_RegKey.DeleteValue(L"Flags"); 316 317 // Notify the dialog all sections are handled. 318 if (hwnd) 319 SendMessageW(hwnd, WM_SETINDEX, m_SectionList.GetSize(), bSuccess); 320 return bSuccess; 321 } 322 323 BOOL RunOnceExInstance::Run(_In_ BOOL bSilence) 324 { 325 if (bSilence || 326 (m_dwFlags & FLAGS_NO_STAT_DIALOG) || 327 !m_bShowDialog) 328 { 329 return Exec(NULL); 330 } 331 else 332 { 333 // The dialog is responsible to create a thread and execute. 334 ProgressDlg dlg(*this); 335 return dlg.RunDialogBox(); 336 } 337 } 338 339 BOOL RunOnceExInstance::HandleSubKey( 340 _In_ CRegKeyEx &hKey, 341 _In_ const CStringW& SubKeyName) 342 { 343 RunOnceExSection Section(hKey, SubKeyName); 344 if (!Section.m_bSuccess) 345 { 346 return FALSE; 347 } 348 349 if (!Section.m_SectionTitle.IsEmpty()) 350 { 351 m_bShowDialog = TRUE; 352 } 353 m_SectionList.Add(Section); 354 355 // The copy constructor of RunOnceExSection didn't detach 356 // the m_RegKey while it's attached to the one in the array. 357 // So we have to detach it manually. 358 Section.m_RegKey.Detach(); 359 return TRUE; 360 } 361