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.

770 lines
21 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. msvctbl.c
  5. Abstract:
  6. Master Service Table routines. Handles all access to the master service
  7. table kept for per-seat information.
  8. =========================== DATA STRUCTURES =============================
  9. MasterServiceTable (PMASTER_SERVICE_RECORD *)
  10. MasterServiceList (PMASTER_SERVICE_RECORD *)
  11. Each of these points to an array of pointers to dynamically allocated
  12. MASTER_SERVICE_RECORDs. There is exactly one MASTER_SERVICE_RECORD
  13. for each (product, version) pairing; e.g., (SQL 4.0, SNA 2.0,
  14. SNA 2.1). The MasterServiceTable is never re-ordered, so a valid
  15. index into this table is guaranteed to always dereference to the same
  16. (product, version). The MasterServiceList contains the same data
  17. sorted lexicographically by product name (and therefore the data
  18. pointed to by a specific index may change over time as new
  19. (product, version) pairs are added to the table). Each table
  20. contains MasterServiceListSize entries.
  21. RootServiceList (PMASTER_SERVICE_ROOT *)
  22. This points to an array of pointers to dynamically allocated
  23. MASTER_SERVICE_ROOTs. There is exactly one MASTER_SERVICE_ROOT
  24. for each product family. Each MASTER_SERVICE_ROOT contains a
  25. pointer to an array of indices into the MasterServiceTable
  26. corresponding to all the products in the family, sorted by
  27. ascending version number. The RootServiceList itself is
  28. sorted lexicographically by ascending family name. It contains
  29. RootServiceListSize entries.
  30. Author:
  31. Arthur Hanson (arth) 07-Dec-1994
  32. Revision History:
  33. Jeff Parham (jeffparh) 05-Dec-1995
  34. o Added a few comments.
  35. o Added parameter to LicenseServiceListFind().
  36. o Fixed benign bug in memory allocation:
  37. sizeof(PULONG) -> sizeof(ULONG).
  38. --*/
  39. #include <stdlib.h>
  40. #include <nt.h>
  41. #include <ntrtl.h>
  42. #include <nturtl.h>
  43. #include <windows.h>
  44. #include <dsgetdc.h>
  45. #include "llsapi.h"
  46. #include "debug.h"
  47. #include "llssrv.h"
  48. #include "registry.h"
  49. #include "ntlsapi.h"
  50. #include "mapping.h"
  51. #include "msvctbl.h"
  52. #include "svctbl.h"
  53. #include "purchase.h"
  54. #include "perseat.h"
  55. #include <strsafe.h> //include last
  56. /////////////////////////////////////////////////////////////////////////
  57. //
  58. // Master Service Table - Keeps list of products (SQL, SNA, etc.) with a
  59. // sub-list for each version of the product.
  60. //
  61. /////////////////////////////////////////////////////////////////////////
  62. /////////////////////////////////////////////////////////////////////////
  63. /////////////////////////////////////////////////////////////////////////
  64. #define DEFAULT_SERVICE_TABLE_ENTRIES 10
  65. ULONG RootServiceListSize = 0;
  66. PMASTER_SERVICE_ROOT *RootServiceList = NULL;
  67. ULONG MasterServiceListSize = 0;
  68. PMASTER_SERVICE_RECORD *MasterServiceList = NULL;
  69. PMASTER_SERVICE_RECORD *MasterServiceTable = NULL;
  70. TCHAR BackOfficeStr[100];
  71. PMASTER_SERVICE_RECORD BackOfficeRec;
  72. RTL_RESOURCE MasterServiceListLock;
  73. HANDLE gLlsDllHandle = NULL;
  74. /////////////////////////////////////////////////////////////////////////
  75. NTSTATUS
  76. MasterServiceListInit()
  77. /*++
  78. Routine Description:
  79. Creates the Master Service table, used for tracking the services and
  80. session count. This will pull the initial services from the registry.
  81. The table is linear so a binary search can be used on the table, so
  82. some extra records are initialized so that each time we add a new
  83. service we don't have to do a realloc. We also assume that adding
  84. new services is a relatively rare occurance, since we need to sort
  85. it each time.
  86. The service table is guarded by a read and write semaphore. Multiple
  87. reads can occur, but a write blocks everything.
  88. The service table has two default entries for FilePrint and REMOTE_ACCESS.
  89. Arguments:
  90. None.
  91. Return Value:
  92. None.
  93. --*/
  94. {
  95. int nLen;
  96. NTSTATUS status = STATUS_SUCCESS;
  97. try
  98. {
  99. RtlInitializeResource(&MasterServiceListLock);
  100. } except(EXCEPTION_EXECUTE_HANDLER ) {
  101. status = GetExceptionCode();
  102. }
  103. if (!NT_SUCCESS(status))
  104. return status;
  105. memset(BackOfficeStr, 0, sizeof(BackOfficeStr));
  106. BackOfficeRec = NULL;
  107. gLlsDllHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
  108. if (gLlsDllHandle != NULL) {
  109. nLen = LoadString(gLlsDllHandle, IDS_BACKOFFICE, BackOfficeStr, sizeof(BackOfficeStr)/sizeof(TCHAR));
  110. if (nLen != 0) {
  111. BackOfficeRec = MasterServiceListAdd( BackOfficeStr, BackOfficeStr, 0 );
  112. if (NULL == BackOfficeRec)
  113. status = STATUS_NO_MEMORY;
  114. } else {
  115. #if DBG
  116. dprintf(TEXT("LLS ERROR: Could not load BackOffice string\n"));
  117. #endif
  118. status = GetLastError();
  119. }
  120. status = GetLastError();
  121. }
  122. return status;
  123. } // MasterServiceListInit
  124. /////////////////////////////////////////////////////////////////////////
  125. // used by qsort to sort MasterServiceList by product name
  126. int __cdecl MasterServiceListCompare(const void *arg1, const void *arg2) {
  127. PMASTER_SERVICE_RECORD Svc1, Svc2;
  128. Svc1 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg1);
  129. Svc2 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg2);
  130. return lstrcmpi( Svc1->Name, Svc2->Name );
  131. } // MasterServiceListCompare
  132. /////////////////////////////////////////////////////////////////////////
  133. // used by qsort to sort the Services array of indices pointed to by the
  134. // MASTER_SERVICE_ROOT structure by product version number
  135. int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2) {
  136. PMASTER_SERVICE_RECORD Svc1, Svc2;
  137. Svc1 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg1)];
  138. Svc2 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg2)];
  139. return (int) Svc1->Version - Svc2->Version;
  140. } // MServiceRecordCompare
  141. /////////////////////////////////////////////////////////////////////////
  142. // used by qsort to sort the RootServiceList array of product families
  143. // by family name
  144. int __cdecl MServiceRootCompare(const void *arg1, const void *arg2) {
  145. PMASTER_SERVICE_ROOT Svc1, Svc2;
  146. Svc1 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg1);
  147. Svc2 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg2);
  148. return lstrcmpi( Svc1->Name, Svc2->Name );
  149. } // MServiceRootCompare
  150. /////////////////////////////////////////////////////////////////////////
  151. PMASTER_SERVICE_ROOT
  152. MServiceRootFind(
  153. LPTSTR ServiceName
  154. )
  155. /*++
  156. Routine Description:
  157. Internal routine to actually do binary search on MasterServiceList, this
  158. does not do any locking as we expect the wrapper routine to do this.
  159. The search is a simple binary search.
  160. Arguments:
  161. ServiceName -
  162. Return Value:
  163. Pointer to found service table entry or NULL if not found.
  164. --*/
  165. {
  166. LONG begin = 0;
  167. LONG end = (LONG) RootServiceListSize - 1;
  168. LONG cur;
  169. int match;
  170. PMASTER_SERVICE_ROOT ServiceRoot;
  171. #if DBG
  172. if (TraceFlags & TRACE_FUNCTION_TRACE)
  173. dprintf(TEXT("LLS TRACE: MServiceRootFind\n"));
  174. #endif
  175. if ((RootServiceListSize == 0) || (ServiceName == NULL))
  176. return NULL;
  177. while (end >= begin) {
  178. // go halfway in-between
  179. cur = (begin + end) / 2;
  180. ServiceRoot = RootServiceList[cur];
  181. // compare the two result into match
  182. match = lstrcmpi(ServiceName, ServiceRoot->Name);
  183. if (match < 0)
  184. // move new begin
  185. end = cur - 1;
  186. else
  187. begin = cur + 1;
  188. if (match == 0)
  189. return ServiceRoot;
  190. }
  191. return NULL;
  192. } // MServiceRootFind
  193. /////////////////////////////////////////////////////////////////////////
  194. PMASTER_SERVICE_RECORD
  195. MasterServiceListFind(
  196. LPTSTR Name
  197. )
  198. /*++
  199. Routine Description:
  200. Internal routine to actually do binary search on MasterServiceList, this
  201. does not do any locking as we expect the wrapper routine to do this.
  202. The search is a simple binary search.
  203. Arguments:
  204. ServiceName -
  205. Return Value:
  206. Pointer to found service table entry or NULL if not found.
  207. --*/
  208. {
  209. LONG begin = 0;
  210. LONG end;
  211. LONG cur;
  212. int match;
  213. PMASTER_SERVICE_RECORD Service;
  214. #if DBG
  215. if (TraceFlags & TRACE_FUNCTION_TRACE)
  216. dprintf(TEXT("LLS TRACE: MasterServiceListFind\n"));
  217. #endif
  218. if ((Name == NULL) || (MasterServiceListSize == 0))
  219. return NULL;
  220. end = (LONG) MasterServiceListSize - 1;
  221. while (end >= begin) {
  222. // go halfway in-between
  223. cur = (begin + end) / 2;
  224. Service = MasterServiceList[cur];
  225. // compare the two result into match
  226. match = lstrcmpi(Name, Service->Name);
  227. if (match < 0)
  228. // move new begin
  229. end = cur - 1;
  230. else
  231. begin = cur + 1;
  232. if (match == 0)
  233. return Service;
  234. }
  235. return NULL;
  236. } // MasterServiceListFind
  237. /////////////////////////////////////////////////////////////////////////
  238. PMASTER_SERVICE_RECORD
  239. MasterServiceListAdd(
  240. LPTSTR FamilyName,
  241. LPTSTR Name,
  242. DWORD Version
  243. )
  244. /*++
  245. Routine Description:
  246. Arguments:
  247. ServiceName -
  248. Return Value:
  249. Pointer to added service table entry, or NULL if failed.
  250. --*/
  251. {
  252. ULONG i;
  253. LPTSTR NewServiceName;
  254. PMASTER_SERVICE_RECORD Service = NULL;
  255. PMASTER_SERVICE_ROOT ServiceRoot = NULL;
  256. PULONG l_pServiceList;
  257. PLICENSE_SERVICE_RECORD pLicense;
  258. PMASTER_SERVICE_ROOT *pRootServiceListTmp;
  259. PULONG pServiceListTmp;
  260. PMASTER_SERVICE_RECORD *pMasterServiceListTmp, *pMasterServiceTableTmp;
  261. HRESULT hr;
  262. size_t cch;
  263. #if DBG
  264. if (TraceFlags & TRACE_FUNCTION_TRACE)
  265. dprintf(TEXT("LLS TRACE: MasterServiceListAdd\n"));
  266. #endif
  267. if ((FamilyName == NULL) || (Name == NULL))
  268. {
  269. return NULL;
  270. }
  271. //
  272. // Mask off low word of version - as it doesn't matter to licensing
  273. //
  274. Version &= 0xFFFF0000;
  275. //
  276. // Try to find a root node for that family of products
  277. //
  278. ServiceRoot = MServiceRootFind(FamilyName);
  279. if (ServiceRoot == NULL) {
  280. //
  281. // No root record - so create a new one
  282. //
  283. if (RootServiceList == NULL)
  284. pRootServiceListTmp = (PMASTER_SERVICE_ROOT *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_ROOT));
  285. else
  286. pRootServiceListTmp = (PMASTER_SERVICE_ROOT *) LocalReAlloc(RootServiceList, sizeof(PMASTER_SERVICE_ROOT) * (RootServiceListSize + 1), LHND);
  287. //
  288. // Make sure we could allocate service table
  289. //
  290. if (pRootServiceListTmp == NULL) {
  291. return NULL;
  292. } else {
  293. RootServiceList = pRootServiceListTmp;
  294. }
  295. //
  296. // Allocate space for Root.
  297. //
  298. ServiceRoot = (PMASTER_SERVICE_ROOT) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_ROOT));
  299. if (ServiceRoot == NULL) {
  300. return NULL;
  301. }
  302. cch = lstrlen(FamilyName) + 1;
  303. NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  304. if (NewServiceName == NULL) {
  305. LocalFree(ServiceRoot);
  306. return NULL;
  307. }
  308. try
  309. {
  310. RtlInitializeResource(&ServiceRoot->ServiceLock);
  311. } except(EXCEPTION_EXECUTE_HANDLER ) {
  312. LocalFree(ServiceRoot);
  313. LocalFree(NewServiceName);
  314. return NULL;
  315. }
  316. RootServiceList[RootServiceListSize] = ServiceRoot;
  317. // now copy it over...
  318. ServiceRoot->Name = NewServiceName;
  319. hr = StringCchCopy(NewServiceName, cch, FamilyName);
  320. ASSERT(SUCCEEDED(hr));
  321. //
  322. // Initialize stuff for list of various versions of this product
  323. //
  324. ServiceRoot->ServiceTableSize = 0;
  325. ServiceRoot->Services = NULL;
  326. ServiceRoot->Flags = 0;
  327. RootServiceListSize++;
  328. // Have added the entry - now need to sort it in order of the service names
  329. qsort((void *) RootServiceList, (size_t) RootServiceListSize, sizeof(PMASTER_SERVICE_ROOT), MServiceRootCompare);
  330. }
  331. RtlAcquireResourceShared(&ServiceRoot->ServiceLock, TRUE);
  332. Service = MasterServiceListFind(Name);
  333. RtlReleaseResource(&ServiceRoot->ServiceLock);
  334. if (Service != NULL)
  335. return Service;
  336. ////////////////////////////////////////////////////////////////////////
  337. //
  338. // Whether added or found, ServiceRoot points to the Root Node entry.
  339. // Now double check to see if another thread just got done adding the
  340. // actual service before we got the write lock.
  341. //
  342. RtlAcquireResourceShared(&ServiceRoot->ServiceLock, TRUE);
  343. Service = MasterServiceListFind(Name);
  344. if (Service == NULL) {
  345. //
  346. // No Service Record - so create a new one
  347. //
  348. RtlConvertSharedToExclusive(&ServiceRoot->ServiceLock);
  349. //
  350. // Double-check that no one snuck in and created it
  351. //
  352. Service = MasterServiceListFind(Name);
  353. if (Service == NULL) {
  354. l_pServiceList = ServiceRoot->Services;
  355. if (l_pServiceList == NULL)
  356. pServiceListTmp = (PULONG) LocalAlloc(LPTR, sizeof(ULONG));
  357. else
  358. pServiceListTmp = (PULONG) LocalReAlloc(l_pServiceList, sizeof(ULONG) * (ServiceRoot->ServiceTableSize + 1), LHND);
  359. if (MasterServiceList == NULL) {
  360. pMasterServiceListTmp = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
  361. pMasterServiceTableTmp = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
  362. } else {
  363. pMasterServiceListTmp = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceList, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
  364. pMasterServiceTableTmp = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceTable, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
  365. }
  366. //
  367. // Make sure we could allocate service table
  368. //
  369. if ((pServiceListTmp == NULL) || (pMasterServiceListTmp == NULL) || (pMasterServiceTableTmp == NULL)) {
  370. if (pServiceListTmp != NULL)
  371. LocalFree(pServiceListTmp);
  372. if (pMasterServiceListTmp != NULL)
  373. LocalFree(pMasterServiceListTmp);
  374. if (pMasterServiceTableTmp != NULL)
  375. LocalFree(pMasterServiceTableTmp);
  376. RtlReleaseResource(&ServiceRoot->ServiceLock);
  377. return NULL;
  378. } else {
  379. l_pServiceList = pServiceListTmp;
  380. MasterServiceList = pMasterServiceListTmp;
  381. MasterServiceTable = pMasterServiceTableTmp;
  382. }
  383. ServiceRoot->Services = l_pServiceList;
  384. //
  385. // Allocate space for saving off Service Record.
  386. //
  387. Service = (PMASTER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_RECORD));
  388. if (Service == NULL) {
  389. ASSERT(FALSE);
  390. RtlReleaseResource(&ServiceRoot->ServiceLock);
  391. return NULL;
  392. }
  393. //
  394. // ...DisplayName
  395. //
  396. cch = lstrlen(Name) + 1;
  397. NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  398. if (NewServiceName == NULL) {
  399. ASSERT(FALSE);
  400. LocalFree(Service);
  401. RtlReleaseResource(&ServiceRoot->ServiceLock);
  402. return NULL;
  403. }
  404. l_pServiceList[ServiceRoot->ServiceTableSize] = MasterServiceListSize;
  405. MasterServiceList[MasterServiceListSize] = Service;
  406. MasterServiceTable[MasterServiceListSize] = Service;
  407. // now copy it over...
  408. Service->Name = NewServiceName;
  409. hr = StringCchCopy(NewServiceName, cch, Name);
  410. ASSERT(SUCCEEDED(hr));
  411. //
  412. // Init rest of values.
  413. //
  414. Service->Version= Version;
  415. Service->LicensesUsed = 0;
  416. Service->LicensesClaimed = 0;
  417. Service->next = 0;
  418. Service->Index = MasterServiceListSize;
  419. Service->Family = ServiceRoot;
  420. pLicense = LicenseServiceListFind(Service->Name, FALSE);
  421. if (pLicense == NULL)
  422. Service->Licenses = 0;
  423. else
  424. Service->Licenses = pLicense->NumberLicenses;
  425. //
  426. // Init next pointer
  427. //
  428. i = 0;
  429. while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < Version))
  430. i++;
  431. if (i > 0) {
  432. Service->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
  433. MasterServiceTable[ServiceRoot->Services[i - 1]]->next = Service->Index + 1;
  434. }
  435. ServiceRoot->ServiceTableSize++;
  436. MasterServiceListSize++;
  437. // Have added the entry - now need to sort it in order of the versions
  438. qsort((void *) l_pServiceList, (size_t) ServiceRoot->ServiceTableSize, sizeof(ULONG), MServiceRecordCompare);
  439. // And sort the list the UI uses (sorted by service name)
  440. qsort((void *) MasterServiceList, (size_t) MasterServiceListSize, sizeof(PMASTER_SERVICE_RECORD), MasterServiceListCompare);
  441. }
  442. }
  443. RtlReleaseResource(&ServiceRoot->ServiceLock);
  444. return Service;
  445. } // MasterServiceListAdd
  446. #if DBG
  447. /////////////////////////////////////////////////////////////////////////
  448. //
  449. // DEBUG INFORMATION DUMP ROUTINES
  450. //
  451. /////////////////////////////////////////////////////////////////////////
  452. /////////////////////////////////////////////////////////////////////////
  453. VOID
  454. MasterServiceRootDebugDump( )
  455. /*++
  456. Routine Description:
  457. Arguments:
  458. Return Value:
  459. --*/
  460. {
  461. ULONG i = 0;
  462. //
  463. // Need to scan list so get read access.
  464. //
  465. RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
  466. dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
  467. if (RootServiceList == NULL)
  468. goto MasterServiceRootDebugDumpExit;
  469. for (i = 0; i < RootServiceListSize; i++) {
  470. dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
  471. i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
  472. }
  473. MasterServiceRootDebugDumpExit:
  474. RtlReleaseResource(&MasterServiceListLock);
  475. return;
  476. } // MasterServiceRootDebugDump
  477. /////////////////////////////////////////////////////////////////////////
  478. VOID
  479. MasterServiceRootDebugInfoDump( PVOID Data )
  480. /*++
  481. Routine Description:
  482. Arguments:
  483. Return Value:
  484. --*/
  485. {
  486. ULONG i = 0;
  487. UNREFERENCED_PARAMETER(Data);
  488. //
  489. // Need to scan list so get read access.
  490. //
  491. RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
  492. dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
  493. if (RootServiceList == NULL)
  494. goto MasterServiceRootDebugDumpExit;
  495. for (i = 0; i < RootServiceListSize; i++) {
  496. dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
  497. i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
  498. }
  499. MasterServiceRootDebugDumpExit:
  500. RtlReleaseResource(&MasterServiceListLock);
  501. return;
  502. } // MasterServiceRootDebugInfoDump
  503. /////////////////////////////////////////////////////////////////////////
  504. VOID
  505. MasterServiceListDebugDump( )
  506. /*++
  507. Routine Description:
  508. Arguments:
  509. Return Value:
  510. --*/
  511. {
  512. ULONG i = 0;
  513. //
  514. // Need to scan list so get read access.
  515. //
  516. RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
  517. dprintf(TEXT("Master Service Table, # Entries: %lu\n"), MasterServiceListSize);
  518. if (MasterServiceList == NULL)
  519. goto MasterServiceListDebugDumpExit;
  520. for (i = 0; i < MasterServiceListSize; i++) {
  521. dprintf(TEXT("%3lu) [%3lu] LU: %4lu LP: %4lu LC: %4lu MS: %4lu HM: %4lu Next: %3lu Svc: %s %lX\n"),
  522. i + 1, MasterServiceList[i]->Index,
  523. MasterServiceList[i]->LicensesUsed, MasterServiceList[i]->Licenses, MasterServiceList[i]->LicensesClaimed,
  524. MasterServiceList[i]->MaxSessionCount, MasterServiceList[i]->HighMark,
  525. MasterServiceList[i]->next, MasterServiceList[i]->Name, MasterServiceList[i]->Version);
  526. }
  527. MasterServiceListDebugDumpExit:
  528. RtlReleaseResource(&MasterServiceListLock);
  529. return;
  530. } // MasterServiceListDebugDump
  531. /////////////////////////////////////////////////////////////////////////
  532. VOID
  533. MasterServiceListDebugInfoDump( PVOID Data )
  534. /*++
  535. Routine Description:
  536. Arguments:
  537. Return Value:
  538. --*/
  539. {
  540. PMASTER_SERVICE_RECORD CurrentRecord = NULL;
  541. dprintf(TEXT("Master Service Table, # Entries: %lu\n"), RootServiceListSize);
  542. if (lstrlen((LPWSTR) Data) > 0) {
  543. // CurrentRecord = MasterServiceListFind((LPWSTR) Data);
  544. if (CurrentRecord != NULL) {
  545. }
  546. }
  547. } // MasterServiceListDebugInfoDump
  548. #endif //DBG