Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

763 lines
22 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. brconfig.c
  5. Abstract:
  6. This module contains the Browser service configuration routines.
  7. Author:
  8. Rita Wong (ritaw) 22-May-1991
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #include <strsafe.h>
  14. //-------------------------------------------------------------------//
  15. // //
  16. // Global variables //
  17. // //
  18. //-------------------------------------------------------------------//
  19. //
  20. // Browser configuration information structure which holds the
  21. // computername, primary domain, browser config buffer, and a resource
  22. // to serialize access to the whole thing.
  23. //
  24. BRCONFIGURATION_INFO BrInfo = {0};
  25. BR_BROWSER_FIELDS BrFields[] = {
  26. {WKSTA_KEYWORD_MAINTAINSRVLST, (LPDWORD) &BrInfo.MaintainServerList,
  27. 1,(DWORD)-1, 0, TriValueType, 0, NULL},
  28. {BROWSER_CONFIG_BACKUP_RECOVERY_TIME, &BrInfo.BackupBrowserRecoveryTime,
  29. BACKUP_BROWSER_RECOVERY_TIME, 0, 0xffffffff, DWordType, 0, NULL},
  30. {L"CacheHitLimit", &BrInfo.CacheHitLimit,
  31. // {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
  32. CACHED_BROWSE_RESPONSE_HIT_LIMIT, 0, 0x100, DWordType, 0, NULL },
  33. {L"CacheResponseSize", &BrInfo.NumberOfCachedResponses,
  34. // {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
  35. CACHED_BROWSE_RESPONSE_LIMIT, 0, MAXULONG, DWordType, 0, NULL },
  36. {L"QueryDriverFrequency", &BrInfo.DriverQueryFrequency,
  37. BROWSER_QUERY_DRIVER_FREQUENCY, 0, 15*60, DWordType, 0, NULL },
  38. {L"DirectHostBinding", (LPDWORD)&BrInfo.DirectHostBinding,
  39. 0, 0, 0, MultiSzType, 0, BrChangeDirectHostBinding },
  40. {L"UnboundBindings", (LPDWORD)&BrInfo.UnboundBindings,
  41. 0, 0, 0, MultiSzType, 0, NULL },
  42. {L"MasterPeriodicity", (LPDWORD)&BrInfo.MasterPeriodicity,
  43. MASTER_PERIODICITY, 5*60, 0x7fffffff/1000, DWordType, 0, BrChangeMasterPeriodicity },
  44. {L"BackupPeriodicity", (LPDWORD)&BrInfo.BackupPeriodicity,
  45. BACKUP_PERIODICITY, 5*60, 0x7fffffff/1000, DWordType, 0, NULL },
  46. {L"BrowserDebug", (LPDWORD) &BrInfo.BrowserDebug,
  47. 0, 0, 0xffffffff,DWordType, 0, NULL},
  48. {L"BrowserDebugLimit", (LPDWORD) &BrInfo.BrowserDebugFileLimit,
  49. 10000*1024, 0, 0xffffffff,DWordType, 0, NULL},
  50. {NULL, NULL, 0, 0, 0, BooleanType, 0, NULL}
  51. };
  52. ULONG
  53. NumberOfServerEnumerations = {0};
  54. ULONG
  55. NumberOfDomainEnumerations = {0};
  56. ULONG
  57. NumberOfOtherEnumerations = {0};
  58. ULONG
  59. NumberOfMissedGetBrowserListRequests = {0};
  60. CRITICAL_SECTION
  61. BrowserStatisticsLock = {0};
  62. NET_API_STATUS
  63. BrGetBrowserConfiguration(
  64. VOID
  65. )
  66. {
  67. NET_API_STATUS status;
  68. NT_PRODUCT_TYPE NtProductType;
  69. try {
  70. //
  71. // Initialize the resource for serializing access to configuration
  72. // information.
  73. //
  74. try{
  75. InitializeCriticalSection(&BrInfo.ConfigCritSect);
  76. }
  77. except ( EXCEPTION_EXECUTE_HANDLER ){
  78. return NERR_NoNetworkResource;
  79. }
  80. //
  81. // Lock config information structure for write access since we are
  82. // initializing the data in the structure.
  83. //
  84. EnterCriticalSection( &BrInfo.ConfigCritSect );
  85. //
  86. // Set pointer to configuration fields structure
  87. //
  88. BrInfo.BrConfigFields = BrFields;
  89. //
  90. // Determine our product type.
  91. //
  92. RtlGetNtProductType(&NtProductType);
  93. BrInfo.IsLanmanNt = (NtProductType == NtProductLanManNt);
  94. //
  95. // Read from the config file the browser configuration fields
  96. //
  97. status = BrReadBrowserConfigFields( TRUE );
  98. if (status != NERR_Success) {
  99. try_return ( status );
  100. }
  101. if (BrInfo.IsLanmanNt) {
  102. BrInfo.MaintainServerList = 1;
  103. }
  104. #ifdef ENABLE_PSEUDO_BROWSER
  105. BrInfo.PseudoServerLevel = GetBrowserPseudoServerLevel();
  106. #endif
  107. //
  108. // Don't let the user define define an incompatible master/backup periodicity.
  109. //
  110. if ( BrInfo.MasterPeriodicity > BrInfo.BackupPeriodicity ) {
  111. BrInfo.BackupPeriodicity = BrInfo.MasterPeriodicity;
  112. }
  113. try_exit:NOTHING;
  114. } finally {
  115. // else
  116. // Leave config file open because we need to read transport names from it.
  117. //
  118. LeaveCriticalSection(&BrInfo.ConfigCritSect);
  119. }
  120. return status;
  121. }
  122. #define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
  123. { \
  124. LPWSTR SubString[1]; \
  125. SubString[0] = lptstrKeyword; \
  126. BrLogEvent(EVENT_BROWSER_ILLEGAL_CONFIG, NERR_Success, 1, SubString); \
  127. NetpKdPrint(( \
  128. "[Browser] *ERROR* Tried to set keyword '" FORMAT_LPTSTR \
  129. "' with invalid value.\n" \
  130. "This error is ignored.\n", \
  131. lptstrKeyword )); \
  132. }
  133. NET_API_STATUS
  134. BrReadBrowserConfigFields(
  135. IN BOOL InitialCall
  136. )
  137. /*++
  138. Routine Description:
  139. This function assigns each browser/redir configuration field to the default
  140. value if it is not specified in the configuration file or if the value
  141. specified in the configuration file is invalid. Otherwise it overrides
  142. the default value with the value found in the configuration file.
  143. Arguments:
  144. InitialCall - True if this call was made during initialization
  145. Return Value:
  146. None.
  147. --*/
  148. {
  149. NET_API_STATUS status;
  150. LPNET_CONFIG_HANDLE BrowserSection;
  151. DWORD i;
  152. LPTSTR KeywordValueBuffer;
  153. DWORD KeywordValueStringLength;
  154. DWORD KeywordValue;
  155. DWORD OldKeywordValue;
  156. //
  157. // Open config file and get handle to the [LanmanBrowser] section
  158. //
  159. if ((status = NetpOpenConfigData(
  160. &BrowserSection,
  161. NULL, // Local
  162. SECT_NT_BROWSER,
  163. TRUE // want read-only access
  164. )) != NERR_Success) {
  165. return status;
  166. }
  167. for (i = 0; BrInfo.BrConfigFields[i].Keyword != NULL; i++) {
  168. BOOL ParameterChanged = FALSE;
  169. //
  170. // Skip this parameter if it can't change dynamically and
  171. // this isn't the initial call.
  172. //
  173. if ( !InitialCall && BrInfo.BrConfigFields[i].DynamicChangeRoutine == NULL ) {
  174. continue;
  175. }
  176. switch (BrInfo.BrConfigFields[i].DataType) {
  177. case MultiSzType:
  178. status = NetpGetConfigTStrArray(
  179. BrowserSection,
  180. BrInfo.BrConfigFields[i].Keyword,
  181. (LPTSTR_ARRAY *)(BrInfo.BrConfigFields[i].FieldPtr));
  182. if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
  183. REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
  184. }
  185. break;
  186. case BooleanType:
  187. status = NetpGetConfigBool(
  188. BrowserSection,
  189. BrInfo.BrConfigFields[i].Keyword,
  190. BrInfo.BrConfigFields[i].Default,
  191. (LPBOOL)(BrInfo.BrConfigFields[i].FieldPtr)
  192. );
  193. if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
  194. REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
  195. }
  196. break;
  197. case TriValueType:
  198. //
  199. // Assign default configuration value
  200. //
  201. *(BrInfo.BrConfigFields[i].FieldPtr) = BrInfo.BrConfigFields[i].Default;
  202. if (NetpGetConfigValue(
  203. BrowserSection,
  204. BrInfo.BrConfigFields[i].Keyword,
  205. &KeywordValueBuffer
  206. ) != NERR_Success) {
  207. continue;
  208. }
  209. KeywordValueStringLength = STRLEN(KeywordValueBuffer);
  210. if (STRICMP(KeywordValueBuffer, KEYWORD_YES) == 0) {
  211. *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
  212. } else if (STRICMP(KeywordValueBuffer, KEYWORD_TRUE) == 0) {
  213. *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
  214. } else if (STRICMP(KeywordValueBuffer, KEYWORD_NO) == 0) {
  215. *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
  216. } else if (STRICMP(KeywordValueBuffer, KEYWORD_FALSE) == 0) {
  217. *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
  218. } else if (STRICMP(KeywordValueBuffer, TEXT("AUTO")) == 0) {
  219. *(BrInfo.BrConfigFields[i].FieldPtr) = 0;
  220. }
  221. else {
  222. REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
  223. }
  224. NetApiBufferFree(KeywordValueBuffer);
  225. break;
  226. case DWordType:
  227. OldKeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
  228. if (NetpGetConfigDword(
  229. BrowserSection,
  230. BrInfo.BrConfigFields[i].Keyword,
  231. BrInfo.BrConfigFields[i].Default,
  232. (LPDWORD)(BrInfo.BrConfigFields[i].FieldPtr)
  233. ) != NERR_Success) {
  234. continue;
  235. }
  236. KeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
  237. //
  238. // Don't allow too large or small a value.
  239. //
  240. if (KeywordValue < BrInfo.BrConfigFields[i].Minimum) {
  241. BrPrint(( BR_CRITICAL, "%ws value out of range %lu (%lu-%lu)\n",
  242. BrInfo.BrConfigFields[i].Keyword, KeywordValue,
  243. BrInfo.BrConfigFields[i].Minimum,
  244. BrInfo.BrConfigFields[i].Maximum
  245. ));
  246. KeywordValue =
  247. *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr =
  248. BrInfo.BrConfigFields[i].Minimum;
  249. }
  250. if (KeywordValue > BrInfo.BrConfigFields[i].Maximum) {
  251. BrPrint(( BR_CRITICAL, "%ws value out of range %lu (%lu-%lu)\n",
  252. BrInfo.BrConfigFields[i].Keyword, KeywordValue,
  253. BrInfo.BrConfigFields[i].Minimum,
  254. BrInfo.BrConfigFields[i].Maximum
  255. ));
  256. KeywordValue =
  257. *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr =
  258. BrInfo.BrConfigFields[i].Maximum;
  259. }
  260. //
  261. // Test if the parameter has actually changed
  262. //
  263. if ( OldKeywordValue != KeywordValue ) {
  264. ParameterChanged = TRUE;
  265. }
  266. break;
  267. default:
  268. NetpAssert(FALSE);
  269. }
  270. //
  271. // If this is a dynamic parameter change,
  272. // and this isn't the initial call.
  273. // notify that this parameter changed.
  274. //
  275. if ( !InitialCall && ParameterChanged ) {
  276. BrInfo.BrConfigFields[i].DynamicChangeRoutine();
  277. }
  278. }
  279. status = NetpCloseConfigData(BrowserSection);
  280. if (BrInfo.DirectHostBinding != NULL &&
  281. !NetpIsTStrArrayEmpty(BrInfo.DirectHostBinding)) {
  282. BrPrint(( BR_INIT,"DirectHostBinding length: %ld\n",NetpTStrArrayEntryCount(BrInfo.DirectHostBinding)));
  283. if (NetpTStrArrayEntryCount(BrInfo.DirectHostBinding) % 2 != 0) {
  284. status = ERROR_INVALID_PARAMETER;
  285. }
  286. }
  287. return status;
  288. }
  289. VOID
  290. BrDeleteConfiguration (
  291. DWORD BrInitState
  292. )
  293. {
  294. if (BrInfo.DirectHostBinding != NULL) {
  295. NetApiBufferFree(BrInfo.DirectHostBinding);
  296. }
  297. if (BrInfo.UnboundBindings != NULL) {
  298. NetApiBufferFree(BrInfo.UnboundBindings);
  299. }
  300. DeleteCriticalSection(&BrInfo.ConfigCritSect);
  301. UNREFERENCED_PARAMETER(BrInitState);
  302. }
  303. NET_API_STATUS
  304. BrChangeDirectHostBinding(
  305. VOID
  306. )
  307. /*++
  308. Routine Description (BrChnageDirectHostBinding):
  309. Handle a change in DirectHostBinding entry in the registry based on
  310. Registry notification.
  311. This is used so that when NwLnkNb transport is created via PnP, we should
  312. also create NwLnkIpx (current usage).
  313. The binding is refreshed in BrReadBrowserConfigFields above.
  314. Arguments:
  315. None.
  316. Return Value:
  317. None.
  318. --*/
  319. {
  320. NET_API_STATUS NetStatus = NERR_Success;
  321. NetStatus = BrChangeConfigValue(
  322. L"DirectHostBinding",
  323. MultiSzType,
  324. NULL,
  325. &(BrInfo.DirectHostBinding),
  326. TRUE );
  327. if ( NetStatus == NERR_Success ) {
  328. //
  329. // DirectHostBinding sepcified. Verify consistency
  330. //
  331. EnterCriticalSection ( &BrInfo.ConfigCritSect );
  332. if (BrInfo.DirectHostBinding != NULL &&
  333. !NetpIsTStrArrayEmpty(BrInfo.DirectHostBinding)) {
  334. BrPrint(( BR_INIT,"DirectHostBinding length: %ld\n",NetpTStrArrayEntryCount(BrInfo.DirectHostBinding)));
  335. if (NetpTStrArrayEntryCount(BrInfo.DirectHostBinding) % 2 != 0) {
  336. NetApiBufferFree(BrInfo.DirectHostBinding);
  337. BrInfo.DirectHostBinding = NULL;
  338. // we fail on invalid specifications
  339. NetStatus = ERROR_INVALID_PARAMETER;
  340. }
  341. }
  342. LeaveCriticalSection ( &BrInfo.ConfigCritSect );
  343. }
  344. return NetStatus;
  345. }
  346. NET_API_STATUS
  347. BrChangeConfigValue(
  348. LPWSTR pszKeyword IN,
  349. DATATYPE dataType IN,
  350. PVOID pDefault IN,
  351. PVOID *ppData OUT,
  352. BOOL bFree IN
  353. )
  354. /*++
  355. Routine Description:
  356. Reads in the registry value for browser registry Entry
  357. Arguments:
  358. pszKeyword -- keyword relative to browser param section
  359. dataType -- the type of the data to get from netapi lib.
  360. pDefault -- Default value (to pass to reg calls).
  361. pData -- data read from the registry.
  362. Return Value:
  363. Net api error code
  364. --*/
  365. {
  366. NET_API_STATUS status = STATUS_SUCCESS;
  367. LPNET_CONFIG_HANDLE BrowserSection = NULL;
  368. LPTSTR KeywordValueBuffer;
  369. DWORD KeywordValueStringLength;
  370. PVOID pData = NULL;
  371. ASSERT ( ppData );
  372. EnterCriticalSection ( &BrInfo.ConfigCritSect );
  373. //
  374. // Open config file and get handle to the [LanmanBrowser] section
  375. //
  376. if ((status = NetpOpenConfigData(
  377. &BrowserSection,
  378. NULL, // Local
  379. SECT_NT_BROWSER,
  380. TRUE // want read-only access
  381. )) != NERR_Success) {
  382. goto Cleanup;
  383. }
  384. switch (dataType) {
  385. case MultiSzType:
  386. {
  387. LPTSTR_ARRAY lpValues = NULL;
  388. status = NetpGetConfigTStrArray(
  389. BrowserSection,
  390. pszKeyword,
  391. (LPTSTR_ARRAY *)(&lpValues));
  392. if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
  393. REPORT_KEYWORD_IGNORED( pszKeyword );
  394. }
  395. else {
  396. pData = (PVOID)lpValues;
  397. }
  398. break;
  399. }
  400. case BooleanType:
  401. {
  402. //
  403. // Note : This case is unused at the moment.
  404. //
  405. BOOL bData;
  406. status = NetpGetConfigBool(
  407. BrowserSection,
  408. pszKeyword,
  409. *(LPBOOL)pDefault,
  410. &bData
  411. );
  412. if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
  413. REPORT_KEYWORD_IGNORED( pszKeyword );
  414. }
  415. else
  416. {
  417. // store bool value in ptr.
  418. // caller is responsible for consistent semantics translation.
  419. pData = IntToPtr((int)bData);
  420. }
  421. break;
  422. }
  423. case TriValueType:
  424. {
  425. //
  426. // Assign default configuration value
  427. //
  428. if (NetpGetConfigValue(
  429. BrowserSection,
  430. pszKeyword,
  431. &KeywordValueBuffer
  432. ) != NERR_Success) {
  433. REPORT_KEYWORD_IGNORED( pszKeyword );
  434. }
  435. KeywordValueStringLength = STRLEN(KeywordValueBuffer);
  436. if (STRICMP(KeywordValueBuffer, KEYWORD_YES) == 0) {
  437. pData = (LPVOID)1;
  438. } else if (STRICMP(KeywordValueBuffer, KEYWORD_TRUE) == 0) {
  439. pData = (LPVOID)1;
  440. } else if (STRICMP(KeywordValueBuffer, KEYWORD_NO) == 0) {
  441. pData = (LPVOID) -1;
  442. } else if (STRICMP(KeywordValueBuffer, KEYWORD_FALSE) == 0) {
  443. pData = (LPVOID) -1;
  444. } else if (STRICMP(KeywordValueBuffer, TEXT("AUTO")) == 0) {
  445. pData = (LPVOID)0;
  446. }
  447. else {
  448. // assign the value pointed by pDefault to pData
  449. pData = ULongToPtr((*(LPDWORD)pDefault));
  450. REPORT_KEYWORD_IGNORED( pszKeyword );
  451. }
  452. NetApiBufferFree(KeywordValueBuffer);
  453. break;
  454. }
  455. case DWordType:
  456. {
  457. DWORD dwTmp;
  458. if (NetpGetConfigDword(
  459. BrowserSection,
  460. pszKeyword,
  461. *(LPDWORD)pDefault,
  462. &dwTmp
  463. ) != NERR_Success) {
  464. REPORT_KEYWORD_IGNORED( pszKeyword );
  465. }
  466. else {
  467. pData = ULongToPtr(dwTmp);
  468. }
  469. break;
  470. }
  471. default:
  472. NetpAssert(FALSE);
  473. }
  474. Cleanup:
  475. // Close config, & leave CS
  476. NetpCloseConfigData(BrowserSection);
  477. // optionaly free data & set return buffer
  478. if ( status == STATUS_SUCCESS )
  479. {
  480. if ( bFree && *ppData )
  481. {
  482. NetApiBufferFree( *ppData );
  483. }
  484. *ppData = pData;
  485. }
  486. LeaveCriticalSection ( &BrInfo.ConfigCritSect );
  487. return status;
  488. }
  489. #if DBG
  490. NET_API_STATUS
  491. BrUpdateDebugInformation(
  492. IN LPWSTR SystemKeyName,
  493. IN LPWSTR ValueName,
  494. IN LPTSTR TransportName,
  495. IN LPTSTR ServerName OPTIONAL,
  496. IN DWORD ServiceStatus
  497. )
  498. /*++
  499. Routine Description:
  500. This routine will stick debug information in the registry about the last
  501. time the browser retrieved information from the remote server.
  502. Arguments:
  503. Return Value:
  504. None.
  505. --*/
  506. {
  507. WCHAR TotalKeyName[MAX_PATH];
  508. ULONG Disposition;
  509. HKEY Key;
  510. ULONG Status;
  511. SYSTEMTIME LocalTime;
  512. WCHAR LastUpdateTime[100];
  513. ULONG AvailableLen, NextLen;
  514. //
  515. // Build the key name:
  516. //
  517. // HKEY_LOCAL_MACHINE:System\CurrentControlSet\Services\Browser\Debug\<Transport>\SystemKeyName
  518. //
  519. AvailableLen = sizeof(TotalKeyName) / sizeof(WCHAR);
  520. wcsncpy(TotalKeyName, L"System\\CurrentControlSet\\Services\\Browser\\Debug", AvailableLen);
  521. AvailableLen -= wcslen(TotalKeyName);
  522. NextLen = wcslen(TransportName);
  523. wcsncat(TotalKeyName, TransportName, AvailableLen);
  524. AvailableLen -= NextLen;
  525. NextLen = wcslen(L"\\");
  526. wcsncat(TotalKeyName, L"\\", AvailableLen);
  527. AvailableLen -= NextLen;
  528. wcsncat(TotalKeyName, SystemKeyName, AvailableLen);
  529. if ((Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TotalKeyName, 0,
  530. L"BrowserDebugInformation",
  531. REG_OPTION_NON_VOLATILE,
  532. KEY_WRITE,
  533. NULL,
  534. &Key,
  535. &Disposition)) != ERROR_SUCCESS) {
  536. BrPrint(( BR_CRITICAL,"Unable to create key to log debug information: %lx\n", Status));
  537. Status;
  538. }
  539. if (ARGUMENT_PRESENT(ServerName)) {
  540. if ((Status = RegSetValueEx(Key, ValueName, 0, REG_SZ, (LPBYTE)ServerName, (wcslen(ServerName)+1) * sizeof(WCHAR))) != ERROR_SUCCESS) {
  541. BrPrint(( BR_CRITICAL,
  542. "Unable to set value of ServerName value to %ws: %lx\n",
  543. ServerName, Status));
  544. RegCloseKey(Key);
  545. return Status;
  546. }
  547. } else {
  548. if ((Status = RegSetValueEx(Key, ValueName, 0, REG_DWORD, (LPBYTE)&ServiceStatus, sizeof(ULONG))) != ERROR_SUCCESS) {
  549. BrPrint(( BR_CRITICAL,"Unable to set value of ServerName value to %ws: %lx\n", ServerName, Status));
  550. RegCloseKey(Key);
  551. return Status;
  552. }
  553. }
  554. GetLocalTime(&LocalTime);
  555. StringCbPrintfW(LastUpdateTime, sizeof(LastUpdateTime), L"%d/%d/%d %d:%d:%d:%d", LocalTime.wDay,
  556. LocalTime.wMonth,
  557. LocalTime.wYear,
  558. LocalTime.wHour,
  559. LocalTime.wMinute,
  560. LocalTime.wSecond,
  561. LocalTime.wMilliseconds);
  562. if ((Status = RegSetValueEx(Key, L"LastUpdateTime", 0, REG_SZ, (LPBYTE)&LastUpdateTime, (wcslen(LastUpdateTime) + 1)*sizeof(WCHAR))) != ERROR_SUCCESS) {
  563. BrPrint(( BR_CRITICAL,"Unable to set value of LastUpdateTime value to %s: %lx\n", LastUpdateTime, Status));
  564. }
  565. RegCloseKey(Key);
  566. return Status;
  567. }
  568. #endif