Source code of Windows XP (NT5)
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.

880 lines
20 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2001 Microsoft Corporation
  3. Module Name :
  4. nt4thunks.cpp
  5. Abstract :
  6. General helper functions to get BITS to work on NT4
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "qmgrlibp.h"
  11. #include <bitsmsg.h>
  12. #include <sddl.h>
  13. #include <shlwapi.h>
  14. #if !defined(BITS_V12_ON_NT4)
  15. #include "nt4thunks.tmh"
  16. #endif
  17. #if defined( BITS_V12_ON_NT4 )
  18. BOOL
  19. BITSAltGetFileSizeEx(
  20. HANDLE hFile, // handle to file
  21. PLARGE_INTEGER lpFileSize // file size
  22. )
  23. {
  24. DWORD HighPart;
  25. DWORD Result =
  26. GetFileSize( hFile, &HighPart );
  27. if ( INVALID_FILE_SIZE == Result &&
  28. GetLastError() != NO_ERROR )
  29. return FALSE;
  30. lpFileSize->HighPart = (LONG)HighPart;
  31. lpFileSize->LowPart = Result;
  32. return TRUE;
  33. }
  34. BOOL
  35. BITSAltSetFilePointerEx(
  36. HANDLE hFile, // handle to file
  37. LARGE_INTEGER liDistanceToMove, // bytes to move pointer
  38. PLARGE_INTEGER lpNewFilePointer, // new file pointer
  39. DWORD dwMoveMethod // starting point
  40. )
  41. {
  42. LONG DistanceToMoveHigh = liDistanceToMove.HighPart;
  43. DWORD DistanceToMoveLow = liDistanceToMove.LowPart;
  44. DWORD Result =
  45. SetFilePointer(
  46. hFile,
  47. (LONG)DistanceToMoveLow,
  48. &DistanceToMoveHigh,
  49. dwMoveMethod );
  50. if ( INVALID_SET_FILE_POINTER == Result &&
  51. NO_ERROR != GetLastError() )
  52. return FALSE;
  53. if ( lpNewFilePointer )
  54. {
  55. lpNewFilePointer->HighPart = DistanceToMoveHigh;
  56. lpNewFilePointer->LowPart = (DWORD)DistanceToMoveLow;
  57. }
  58. return TRUE;
  59. }
  60. //
  61. // Local macros
  62. //
  63. #define STRING_GUID_LEN 36
  64. #define STRING_GUID_SIZE ( STRING_GUID_LEN * sizeof( WCHAR ) )
  65. #define SDDL_LEN_TAG( tagdef ) ( sizeof( tagdef ) / sizeof( WCHAR ) - 1 )
  66. #define SDDL_SIZE_TAG( tagdef ) ( wcslen( tagdef ) * sizeof( WCHAR ) )
  67. #define SDDL_SIZE_SEP( sep ) (sizeof( WCHAR ) )
  68. #define SDDL_VALID_DACL 0x00000001
  69. #define SDDL_VALID_SACL 0x00000002
  70. ULONG
  71. BITSAltSetLastNTError(
  72. IN NTSTATUS Status
  73. )
  74. {
  75. ULONG dwErrorCode;
  76. dwErrorCode = RtlNtStatusToDosError( Status );
  77. SetLastError( dwErrorCode );
  78. return( dwErrorCode );
  79. }
  80. BOOL
  81. BITSAltConvertSidToStringSidW(
  82. IN PSID Sid,
  83. OUT LPWSTR *StringSid
  84. )
  85. /*++
  86. Routine Description:
  87. This routine converts a SID into a string representation of a SID, suitable for framing or
  88. display
  89. Arguments:
  90. Sid - SID to be converted.
  91. StringSid - Where the converted SID is returned. Allocated via LocalAlloc and needs to
  92. be freed via LocalFree.
  93. Return Value:
  94. TRUE - Success
  95. FALSE - Failure
  96. Extended error status is available using GetLastError.
  97. --*/
  98. {
  99. NTSTATUS Status;
  100. UNICODE_STRING UnicodeStringSid;
  101. if ( NULL == Sid || NULL == StringSid ) {
  102. //
  103. // invalid parameter
  104. //
  105. SetLastError( ERROR_INVALID_PARAMETER );
  106. return( FALSE );
  107. }
  108. //
  109. // Convert using the Rtl functions
  110. //
  111. Status = RtlConvertSidToUnicodeString( &UnicodeStringSid, Sid, TRUE );
  112. if ( !NT_SUCCESS( Status ) ) {
  113. BITSAltSetLastNTError( Status );
  114. return( FALSE );
  115. }
  116. //
  117. // Convert it to the proper allocator
  118. //
  119. *StringSid = (LPWSTR)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
  120. UnicodeStringSid.Length + sizeof( WCHAR ) );
  121. if ( *StringSid == NULL ) {
  122. RtlFreeUnicodeString( &UnicodeStringSid );
  123. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  124. return( FALSE );
  125. }
  126. RtlCopyMemory( *StringSid, UnicodeStringSid.Buffer, UnicodeStringSid.Length );
  127. RtlFreeUnicodeString( &UnicodeStringSid );
  128. SetLastError(ERROR_SUCCESS);
  129. return( TRUE );
  130. }
  131. //
  132. // Private functions
  133. //
  134. BOOL
  135. LocalConvertStringSidToSid (
  136. IN PWSTR StringSid,
  137. OUT PSID *Sid,
  138. OUT PWSTR *End
  139. )
  140. /*++
  141. Routine Description:
  142. This routine will convert a string representation of a SID back into
  143. a sid. The expected format of the string is:
  144. "S-1-5-32-549"
  145. If a string in a different format or an incorrect or incomplete string
  146. is given, the operation is failed.
  147. The returned sid must be free via a call to LocalFree
  148. Arguments:
  149. StringSid - The string to be converted
  150. Sid - Where the created SID is to be returned
  151. End - Where in the string we stopped processing
  152. Return Value:
  153. TRUE - Success.
  154. FALSE - Failure. Additional information returned from GetLastError(). Errors set are:
  155. ERROR_SUCCESS indicates success
  156. ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput sid
  157. failed
  158. ERROR_INVALID_SID indicates that the given string did not represent a sid
  159. --*/
  160. {
  161. DWORD Err = ERROR_SUCCESS;
  162. UCHAR Revision, Subs;
  163. SID_IDENTIFIER_AUTHORITY IDAuth;
  164. PULONG SubAuth = NULL;
  165. PWSTR CurrEnd, Curr, Next;
  166. WCHAR Stub, *StubPtr = NULL;
  167. ULONG Index;
  168. INT gBase=10;
  169. INT lBase=10;
  170. ULONG Auto;
  171. if ( NULL == StringSid || NULL == Sid || NULL == End ) {
  172. SetLastError( ERROR_INVALID_PARAMETER );
  173. return( FALSE );
  174. }
  175. // if ( wcslen( StringSid ) < 2 || ( *StringSid != L'S' && *( StringSid + 1 ) != L'-' ) ) {
  176. //
  177. // no need to check length because StringSid is NULL
  178. // and if the first char is NULL, it won't access the second char
  179. //
  180. if ( (*StringSid != L'S' && *StringSid != L's') ||
  181. *( StringSid + 1 ) != L'-' ) {
  182. //
  183. // string sid should always start with S-
  184. //
  185. SetLastError( ERROR_INVALID_SID );
  186. return( FALSE );
  187. }
  188. Curr = StringSid + 2;
  189. if ( (*Curr == L'0') &&
  190. ( *(Curr+1) == L'x' ||
  191. *(Curr+1) == L'X' ) ) {
  192. gBase = 16;
  193. }
  194. Revision = ( UCHAR )wcstol( Curr, &CurrEnd, gBase );
  195. if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) {
  196. //
  197. // no revision is provided, or invalid delimeter
  198. //
  199. SetLastError( ERROR_INVALID_SID );
  200. return( FALSE );
  201. }
  202. Curr = CurrEnd + 1;
  203. //
  204. // Count the number of characters in the indentifer authority...
  205. //
  206. Next = wcschr( Curr, L'-' );
  207. /*
  208. Length = 6 doesn't mean each digit is a id authority value, could be 0x...
  209. if ( Next != NULL && (Next - Curr == 6) ) {
  210. for ( Index = 0; Index < 6; Index++ ) {
  211. // IDAuth.Value[Index] = (UCHAR)Next[Index]; what is this ???
  212. IDAuth.Value[Index] = (BYTE) (Curr[Index]-L'0');
  213. }
  214. Curr +=6;
  215. } else {
  216. */
  217. if ( (*Curr == L'0') &&
  218. ( *(Curr+1) == L'x' ||
  219. *(Curr+1) == L'X' ) ) {
  220. lBase = 16;
  221. } else {
  222. lBase = gBase;
  223. }
  224. Auto = wcstoul( Curr, &CurrEnd, lBase );
  225. if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) {
  226. //
  227. // no revision is provided, or invalid delimeter
  228. //
  229. SetLastError( ERROR_INVALID_SID );
  230. return( FALSE );
  231. }
  232. IDAuth.Value[0] = IDAuth.Value[1] = 0;
  233. IDAuth.Value[5] = ( UCHAR )Auto & 0xFF;
  234. IDAuth.Value[4] = ( UCHAR )(( Auto >> 8 ) & 0xFF );
  235. IDAuth.Value[3] = ( UCHAR )(( Auto >> 16 ) & 0xFF );
  236. IDAuth.Value[2] = ( UCHAR )(( Auto >> 24 ) & 0xFF );
  237. Curr = CurrEnd;
  238. // }
  239. //
  240. // Now, count the number of sub auths, at least one sub auth is required
  241. //
  242. Subs = 0;
  243. Next = Curr;
  244. //
  245. // We'll have to count our sub authoritys one character at a time,
  246. // since there are several deliminators that we can have...
  247. //
  248. while ( Next ) {
  249. if ( *Next == L'-' && *(Next-1) != L'-') {
  250. //
  251. // do not allow two continuous '-'s
  252. // We've found one!
  253. //
  254. Subs++;
  255. if ( (*(Next+1) == L'0') &&
  256. ( *(Next+2) == L'x' ||
  257. *(Next+2) == L'X' ) ) {
  258. //
  259. // this is hex indicator
  260. //
  261. Next += 2;
  262. }
  263. } else if ( *Next == SDDL_SEPERATORC || *Next == L'\0' ||
  264. *Next == SDDL_ACE_ENDC || *Next == L' ' ||
  265. ( *(Next+1) == SDDL_DELIMINATORC &&
  266. (*Next == L'G' || *Next == L'O' || *Next == L'S')) ) {
  267. //
  268. // space is a terminator too
  269. //
  270. if ( *( Next - 1 ) == L'-' ) {
  271. //
  272. // shouldn't allow a SID terminated with '-'
  273. //
  274. Err = ERROR_INVALID_SID;
  275. Next--;
  276. } else {
  277. Subs++;
  278. }
  279. *End = Next;
  280. break;
  281. } else if ( !iswxdigit( *Next ) ) {
  282. Err = ERROR_INVALID_SID;
  283. *End = Next;
  284. // Subs++;
  285. break;
  286. } else {
  287. //
  288. // Note: SID is also used as a owner or group
  289. //
  290. // Some of the tags (namely 'D' for Dacl) fall under the category of iswxdigit, so
  291. // if the current character is a character we care about and the next one is a
  292. // delminiator, we'll quit
  293. //
  294. if ( *Next == L'D' && *( Next + 1 ) == SDDL_DELIMINATORC ) {
  295. //
  296. // We'll also need to temporarily truncate the string to this length so
  297. // we don't accidentally include the character in one of the conversions
  298. //
  299. Stub = *Next;
  300. StubPtr = Next;
  301. *StubPtr = UNICODE_NULL;
  302. *End = Next;
  303. Subs++;
  304. break;
  305. }
  306. }
  307. Next++;
  308. }
  309. if ( Err == ERROR_SUCCESS ) {
  310. if ( Subs != 0 ) Subs--;
  311. if ( Subs != 0 ) {
  312. Curr++;
  313. SubAuth = ( PULONG )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Subs * sizeof( ULONG ) );
  314. if ( SubAuth == NULL ) {
  315. Err = ERROR_NOT_ENOUGH_MEMORY;
  316. } else {
  317. for ( Index = 0; Index < Subs; Index++ ) {
  318. if ( (*Curr == L'0') &&
  319. ( *(Curr+1) == L'x' ||
  320. *(Curr+1) == L'X' ) ) {
  321. lBase = 16;
  322. } else {
  323. lBase = gBase;
  324. }
  325. SubAuth[Index] = wcstoul( Curr, &CurrEnd, lBase );
  326. Curr = CurrEnd + 1;
  327. }
  328. }
  329. } else {
  330. Err = ERROR_INVALID_SID;
  331. }
  332. }
  333. //
  334. // Now, create the SID
  335. //
  336. if ( Err == ERROR_SUCCESS ) {
  337. *Sid = ( PSID )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
  338. sizeof( SID ) + Subs * sizeof( ULONG ) );
  339. if ( *Sid == NULL ) {
  340. Err = ERROR_NOT_ENOUGH_MEMORY;
  341. } else {
  342. PISID ISid = ( PISID )*Sid;
  343. ISid->Revision = Revision;
  344. ISid->SubAuthorityCount = Subs;
  345. RtlCopyMemory( &( ISid->IdentifierAuthority ), &IDAuth,
  346. sizeof( SID_IDENTIFIER_AUTHORITY ) );
  347. RtlCopyMemory( ISid->SubAuthority, SubAuth, Subs * sizeof( ULONG ) );
  348. }
  349. }
  350. LocalFree( SubAuth );
  351. //
  352. // Restore any character we may have stubbed out
  353. //
  354. if ( StubPtr ) {
  355. *StubPtr = Stub;
  356. }
  357. SetLastError( Err );
  358. return( Err == ERROR_SUCCESS );
  359. }
  360. BOOL
  361. BITSAltConvertStringSidToSidW(
  362. IN LPCWSTR StringSid,
  363. OUT PSID *Sid
  364. )
  365. /*++
  366. Routine Description:
  367. This routine converts a stringized SID into a valid, functional SID
  368. Arguments:
  369. StringSid - SID to be converted.
  370. Sid - Where the converted SID is returned. Buffer is allocated via LocalAlloc and should
  371. be free via LocalFree.
  372. Return Value:
  373. TRUE - Success
  374. FALSE - Failure
  375. Extended error status is available using GetLastError.
  376. ERROR_INVALID_PARAMETER - A NULL name was given
  377. ERROR_INVALID_SID - The format of the given sid was incorrect
  378. --*/
  379. {
  380. PWSTR End = NULL;
  381. BOOL ReturnValue = FALSE;
  382. PSID pSASid=NULL;
  383. ULONG Len=0;
  384. DWORD SaveCode=0;
  385. DWORD Err=0;
  386. if ( StringSid == NULL || Sid == NULL )
  387. {
  388. SetLastError( ERROR_INVALID_PARAMETER );
  389. return ReturnValue;
  390. }
  391. ReturnValue = LocalConvertStringSidToSid( ( PWSTR )StringSid, Sid, &End );
  392. if ( !ReturnValue )
  393. {
  394. SetLastError( ERROR_INVALID_PARAMETER );
  395. return ReturnValue;
  396. }
  397. if ( ( ULONG )( End - StringSid ) != wcslen( StringSid ) ) {
  398. SetLastError( ERROR_INVALID_SID );
  399. LocalFree( *Sid );
  400. *Sid = FALSE;
  401. ReturnValue = FALSE;
  402. } else {
  403. SetLastError(ERROR_SUCCESS);
  404. }
  405. return ReturnValue;
  406. }
  407. BOOL
  408. BITSAltCheckTokenMembership(
  409. IN HANDLE TokenHandle OPTIONAL,
  410. IN PSID SidToCheck,
  411. OUT PBOOL IsMember
  412. )
  413. /*++
  414. Routine Description:
  415. This function checks to see whether the specified sid is enabled in
  416. the specified token.
  417. Arguments:
  418. TokenHandle - If present, this token is checked for the sid. If not
  419. present then the current effective token will be used. This must
  420. be an impersonation token.
  421. SidToCheck - The sid to check for presence in the token
  422. IsMember - If the sid is enabled in the token, contains TRUE otherwise
  423. false.
  424. Return Value:
  425. TRUE - The API completed successfully. It does not indicate that the
  426. sid is a member of the token.
  427. FALSE - The API failed. A more detailed status code can be retrieved
  428. via GetLastError()
  429. --*/
  430. {
  431. HANDLE ProcessToken = NULL;
  432. HANDLE EffectiveToken = NULL;
  433. NTSTATUS Status = STATUS_SUCCESS;
  434. PISECURITY_DESCRIPTOR SecDesc = NULL;
  435. ULONG SecurityDescriptorSize;
  436. GENERIC_MAPPING GenericMapping = {
  437. STANDARD_RIGHTS_READ,
  438. STANDARD_RIGHTS_EXECUTE,
  439. STANDARD_RIGHTS_WRITE,
  440. STANDARD_RIGHTS_ALL };
  441. //
  442. // The size of the privilege set needs to contain the set itself plus
  443. // any privileges that may be used. The privileges that are used
  444. // are SeTakeOwnership and SeSecurity, plus one for good measure
  445. //
  446. BYTE PrivilegeSetBuffer[sizeof(PRIVILEGE_SET) + 3*sizeof(LUID_AND_ATTRIBUTES)];
  447. PPRIVILEGE_SET PrivilegeSet = (PPRIVILEGE_SET) PrivilegeSetBuffer;
  448. ULONG PrivilegeSetLength = sizeof(PrivilegeSetBuffer);
  449. ACCESS_MASK AccessGranted = 0;
  450. NTSTATUS AccessStatus = 0;
  451. PACL Dacl = NULL;
  452. #define MEMBER_ACCESS 1
  453. *IsMember = FALSE;
  454. //
  455. // Get a handle to the token
  456. //
  457. if (ARGUMENT_PRESENT(TokenHandle))
  458. {
  459. EffectiveToken = TokenHandle;
  460. }
  461. else
  462. {
  463. Status = NtOpenThreadToken(
  464. NtCurrentThread(),
  465. TOKEN_QUERY,
  466. FALSE, // don't open as self
  467. &EffectiveToken
  468. );
  469. //
  470. // if there is no thread token, try the process token
  471. //
  472. if (Status == STATUS_NO_TOKEN)
  473. {
  474. Status = NtOpenProcessToken(
  475. NtCurrentProcess(),
  476. TOKEN_QUERY | TOKEN_DUPLICATE,
  477. &ProcessToken
  478. );
  479. //
  480. // If we have a process token, we need to convert it to an
  481. // impersonation token
  482. //
  483. if (NT_SUCCESS(Status))
  484. {
  485. BOOL Result;
  486. Result = DuplicateToken(
  487. ProcessToken,
  488. SecurityImpersonation,
  489. &EffectiveToken
  490. );
  491. CloseHandle(ProcessToken);
  492. if (!Result)
  493. {
  494. return(FALSE);
  495. }
  496. }
  497. }
  498. if (!NT_SUCCESS(Status))
  499. {
  500. goto Cleanup;
  501. }
  502. }
  503. //
  504. // Construct a security descriptor to pass to access check
  505. //
  506. //
  507. // The size is equal to the size of an SD + twice the length of the SID
  508. // (for owner and group) + size of the DACL = sizeof ACL + size of the
  509. // ACE, which is an ACE + length of
  510. // ths SID.
  511. //
  512. SecurityDescriptorSize = sizeof(SECURITY_DESCRIPTOR) +
  513. sizeof(ACCESS_ALLOWED_ACE) +
  514. sizeof(ACL) +
  515. 3 * RtlLengthSid(SidToCheck);
  516. SecDesc = (PISECURITY_DESCRIPTOR) LocalAlloc(LMEM_ZEROINIT, SecurityDescriptorSize );
  517. if (SecDesc == NULL)
  518. {
  519. Status = STATUS_INSUFFICIENT_RESOURCES;
  520. goto Cleanup;
  521. }
  522. Dacl = (PACL) (SecDesc + 1);
  523. RtlCreateSecurityDescriptor(
  524. SecDesc,
  525. SECURITY_DESCRIPTOR_REVISION
  526. );
  527. //
  528. // Fill in fields of security descriptor
  529. //
  530. RtlSetOwnerSecurityDescriptor(
  531. SecDesc,
  532. SidToCheck,
  533. FALSE
  534. );
  535. RtlSetGroupSecurityDescriptor(
  536. SecDesc,
  537. SidToCheck,
  538. FALSE
  539. );
  540. Status = RtlCreateAcl(
  541. Dacl,
  542. SecurityDescriptorSize - sizeof(SECURITY_DESCRIPTOR),
  543. ACL_REVISION
  544. );
  545. if (!NT_SUCCESS(Status))
  546. {
  547. goto Cleanup;
  548. }
  549. Status = RtlAddAccessAllowedAce(
  550. Dacl,
  551. ACL_REVISION,
  552. MEMBER_ACCESS,
  553. SidToCheck
  554. );
  555. if (!NT_SUCCESS(Status))
  556. {
  557. goto Cleanup;
  558. }
  559. //
  560. // Set the DACL on the security descriptor
  561. //
  562. Status = RtlSetDaclSecurityDescriptor(
  563. SecDesc,
  564. TRUE, // DACL present
  565. Dacl,
  566. FALSE // not defaulted
  567. );
  568. if (!NT_SUCCESS(Status))
  569. {
  570. goto Cleanup;
  571. }
  572. Status = NtAccessCheck(
  573. SecDesc,
  574. EffectiveToken,
  575. MEMBER_ACCESS,
  576. &GenericMapping,
  577. PrivilegeSet,
  578. &PrivilegeSetLength,
  579. &AccessGranted,
  580. &AccessStatus
  581. );
  582. if (!NT_SUCCESS(Status))
  583. {
  584. goto Cleanup;
  585. }
  586. //
  587. // if the access check failed, then the sid is not a member of the
  588. // token
  589. //
  590. if ((AccessStatus == STATUS_SUCCESS) && (AccessGranted == MEMBER_ACCESS))
  591. {
  592. *IsMember = TRUE;
  593. }
  594. Cleanup:
  595. if (!ARGUMENT_PRESENT(TokenHandle) && (EffectiveToken != NULL))
  596. {
  597. (VOID) NtClose(EffectiveToken);
  598. }
  599. if (SecDesc != NULL)
  600. {
  601. LocalFree(SecDesc);
  602. }
  603. if (!NT_SUCCESS(Status))
  604. {
  605. BITSAltSetLastNTError(Status);
  606. return(FALSE);
  607. }
  608. else
  609. {
  610. return(TRUE);
  611. }
  612. }
  613. LPHANDLER_FUNCTION_EX g_BITSAltRegisterServiceFunc = NULL;
  614. typedef SERVICE_STATUS_HANDLE (*REGISTER_FUNC_TYPE)(LPCTSTR, LPHANDLER_FUNCTION_EX, LPVOID lpContext);
  615. VOID WINAPI
  616. BITSAltRegisterServiceThunk(
  617. DWORD dwControl // requested control code
  618. )
  619. {
  620. (*g_BITSAltRegisterServiceFunc)( dwControl, 0, NULL, NULL );
  621. return;
  622. }
  623. SERVICE_STATUS_HANDLE
  624. BITSAltRegisterServiceCtrlHandlerExW(
  625. LPCTSTR lpServiceName, // name of service
  626. LPHANDLER_FUNCTION_EX lpHandlerProc, // handler function
  627. LPVOID lpContext // user data
  628. )
  629. {
  630. // First check if RegisterServerCtrlHandlerEx if available and use
  631. // it, otherwise thunk the call.
  632. HMODULE AdvapiHandle = LoadLibraryW( L"advapi32.dll" );
  633. if ( !AdvapiHandle )
  634. {
  635. // Something is messed up, every machine should have this DLL.
  636. return NULL;
  637. }
  638. SERVICE_STATUS_HANDLE ReturnValue;
  639. FARPROC RegisterFunc = GetProcAddress( AdvapiHandle, "RegisterServiceCtrlHandlerExW" );
  640. if ( RegisterFunc )
  641. {
  642. ReturnValue = (*(REGISTER_FUNC_TYPE)RegisterFunc)( lpServiceName, lpHandlerProc, lpContext );
  643. }
  644. else
  645. {
  646. if ( g_BITSAltRegisterServiceFunc || lpContext )
  647. {
  648. ReturnValue = 0;
  649. SetLastError( ERROR_INVALID_PARAMETER );
  650. }
  651. else
  652. {
  653. g_BITSAltRegisterServiceFunc = lpHandlerProc;
  654. ReturnValue = RegisterServiceCtrlHandler( lpServiceName, BITSAltRegisterServiceThunk );
  655. if ( !ReturnValue)
  656. g_BITSAltRegisterServiceFunc = NULL;
  657. }
  658. }
  659. DWORD OldError = GetLastError();
  660. FreeLibrary( AdvapiHandle );
  661. SetLastError( OldError );
  662. return ReturnValue;
  663. }
  664. #endif