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.

769 lines
21 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. init.c
  5. Abstract:
  6. This is the init/term entry points for the user mode library of the
  7. user mode reflector. This implements UMReflectorRegister,
  8. UMReflectorUnregister, & UMReflectorReleaseThreads.
  9. Author:
  10. Andy Herron (andyhe) 19-Apr-1999
  11. Environment:
  12. User Mode - Win32
  13. Revision History:
  14. --*/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #include <shlobj.h>
  18. typedef
  19. BOOL
  20. (*PFN_GETWININET_CACHE_PATH) (
  21. HWND hwnd,
  22. LPWSTR pszPath,
  23. int csidl,
  24. BOOL fCreate
  25. );
  26. ULONG
  27. UMReflectorRegister (
  28. PWCHAR DriverDeviceName,
  29. ULONG ReflectorVersion,
  30. PUMRX_USERMODE_REFLECT_BLOCK *Reflector
  31. )
  32. /*++
  33. Routine Description:
  34. This routine registers the user mode process with the kernel mode component.
  35. We'll register this user mode process with the driver's reflector.
  36. Arguments:
  37. DriverDeviceName - Must be a valid name of the form L"\\Device\\foobar",
  38. where foobar is the device name registered with
  39. RxRegisterMinirdr.
  40. ReflectorVersion - The version of the library.
  41. Reflector - This is returned by the call and points to an opaque structure
  42. that should be passed to subsequent calls.
  43. Return Value:
  44. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  45. success.
  46. --*/
  47. {
  48. ULONG rc = STATUS_SUCCESS;
  49. ULONG sizeRequired;
  50. PUMRX_USERMODE_REFLECT_BLOCK reflectorInstance = NULL;
  51. UNICODE_STRING UMRxDeviceName;
  52. UNICODE_STRING DeviceObjectName;
  53. IO_STATUS_BLOCK IoStatusBlock;
  54. OBJECT_ATTRIBUTES ObjectAttributes;
  55. ULONG driverDeviceNameLength;
  56. if (ReflectorVersion != UMREFLECTOR_CURRENT_VERSION) {
  57. //
  58. // Whoops. Mismatch here. We should support backward levels but right
  59. // now there aren't any so we just bail.
  60. //
  61. rc = ERROR_NOT_SUPPORTED;
  62. goto errorExit;
  63. }
  64. if (DriverDeviceName == NULL || Reflector == NULL) {
  65. rc = ERROR_INVALID_PARAMETER;
  66. goto errorExit;
  67. }
  68. //
  69. // Calculate the size to be allocated for the UMRX_USERMODE_REFLECT_BLOCK
  70. // and the device name following it.
  71. //
  72. sizeRequired = sizeof(UMRX_USERMODE_REFLECT_BLOCK);
  73. driverDeviceNameLength = lstrlenW(DriverDeviceName) + 1;
  74. sizeRequired += driverDeviceNameLength * sizeof(WCHAR);
  75. reflectorInstance = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeRequired);
  76. *Reflector = reflectorInstance;
  77. if (reflectorInstance == NULL) {
  78. rc = ERROR_NOT_ENOUGH_MEMORY;
  79. goto errorExit;
  80. }
  81. try {
  82. InitializeCriticalSection( &(reflectorInstance->Lock) );
  83. } except(EXCEPTION_EXECUTE_HANDLER) {
  84. rc = GetExceptionCode();
  85. RlDavDbgPrint(("%ld: ERROR: UMReflectorRegister/InitializeCriticalSection: "
  86. "Exception Code = %08lx\n", GetCurrentThreadId(), rc));
  87. goto errorExit;
  88. }
  89. InitializeListHead(&reflectorInstance->WorkerList);
  90. InitializeListHead(&reflectorInstance->WorkItemList);
  91. InitializeListHead(&reflectorInstance->AvailableList);
  92. //
  93. // For being alive add a reference to the block.
  94. //
  95. reflectorInstance->ReferenceCount = 1;
  96. reflectorInstance->Closing = FALSE;
  97. reflectorInstance->DeviceHandle = INVALID_HANDLE_VALUE;
  98. //
  99. // We copy the driver names into the bottom of our buffer so that we have
  100. // copies of them later on if needed.
  101. //
  102. reflectorInstance->DriverDeviceName = &reflectorInstance->DeviceNameBuffers[0];
  103. lstrcpyW(reflectorInstance->DriverDeviceName, DriverDeviceName);
  104. //
  105. // Attempt to connect up with the driver.
  106. //
  107. RtlInitUnicodeString(&UMRxDeviceName, reflectorInstance->DriverDeviceName);
  108. InitializeObjectAttributes(&ObjectAttributes,
  109. &UMRxDeviceName,
  110. OBJ_CASE_INSENSITIVE,
  111. NULL,
  112. NULL);
  113. rc = NtOpenFile(&reflectorInstance->DeviceHandle,
  114. SYNCHRONIZE,
  115. &ObjectAttributes,
  116. &IoStatusBlock,
  117. FILE_SHARE_VALID_FLAGS,
  118. FILE_SYNCHRONOUS_IO_NONALERT);
  119. if (rc == STATUS_SUCCESS) {
  120. ASSERT( reflectorInstance->DeviceHandle != INVALID_HANDLE_VALUE );
  121. } else {
  122. rc = RtlNtStatusToDosError(rc);
  123. }
  124. errorExit:
  125. if (rc != STATUS_SUCCESS) {
  126. //
  127. // Things failed here. Let's clean up.
  128. //
  129. (void) UMReflectorUnregister(reflectorInstance);
  130. *Reflector = NULL;
  131. }
  132. return rc;
  133. }
  134. VOID
  135. DereferenceReflectorBlock (
  136. PUMRX_USERMODE_REFLECT_BLOCK Reflector
  137. )
  138. /*++
  139. Routine Description:
  140. This routine dereferences the reflector block and if the reference becomes
  141. zero, finalizes it.
  142. Arguments:
  143. Reflector - This is returned by the call and points to an opaque structure
  144. that should be passed to subsequent calls.
  145. Return Value:
  146. none.
  147. --*/
  148. {
  149. PLIST_ENTRY listEntry;
  150. PUMRX_USERMODE_WORKITEM_ADDON workItem;
  151. //
  152. // The lock MUST be held coming in here. This could free the block.
  153. //
  154. if (--Reflector->ReferenceCount > 0) {
  155. LeaveCriticalSection(&Reflector->Lock);
  156. return;
  157. }
  158. //
  159. // We're done with this block now, so let's delete it.
  160. //
  161. RlDavDbgPrint(("%ld: Finalizing the Reflector BLock: %08lx.\n",
  162. GetCurrentThreadId(), Reflector));
  163. LeaveCriticalSection(&Reflector->Lock);
  164. DeleteCriticalSection(&Reflector->Lock);
  165. if (Reflector->DeviceHandle != INVALID_HANDLE_VALUE) {
  166. NtClose(Reflector->DeviceHandle);
  167. Reflector->DeviceHandle = INVALID_HANDLE_VALUE;
  168. }
  169. //
  170. // The work item list at this point really should be empty. If it isn't,
  171. // we're hosed as we've closed the device and shutdown all threads.
  172. //
  173. ASSERT(IsListEmpty(&Reflector->WorkItemList));
  174. //
  175. // Free up the AvailableList since this instance is now history.
  176. //
  177. while (!IsListEmpty(&Reflector->AvailableList)) {
  178. listEntry = RemoveHeadList(&Reflector->AvailableList);
  179. workItem = CONTAINING_RECORD(listEntry,
  180. UMRX_USERMODE_WORKITEM_ADDON,
  181. ListEntry);
  182. workItem->WorkItemState = WorkItemStateFree;
  183. LocalFree(workItem);
  184. }
  185. LocalFree(Reflector);
  186. return;
  187. }
  188. ULONG
  189. UMReflectorUnregister (
  190. PUMRX_USERMODE_REFLECT_BLOCK Reflector
  191. )
  192. /*++
  193. Routine Description:
  194. Unregister us with the kernel driver and free all resources.
  195. Arguments:
  196. Handle - The handle created by the reflector library.
  197. Return Value:
  198. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  199. success.
  200. --*/
  201. {
  202. IO_STATUS_BLOCK IoStatusBlock;
  203. UNICODE_STRING UMRxDeviceName;
  204. OBJECT_ATTRIBUTES ObjectAttributes;
  205. HANDLE UMRdrHandle;
  206. ULONG rc = ERROR_SUCCESS;
  207. if (Reflector == NULL) {
  208. return ERROR_INVALID_PARAMETER;
  209. }
  210. Reflector->Closing = TRUE;
  211. // rc = UMReflectorReleaseThreads(Reflector);
  212. EnterCriticalSection(&Reflector->Lock);
  213. //
  214. // If we don't have any worker threads active, delete this guy now.
  215. //
  216. DereferenceReflectorBlock(Reflector);
  217. return rc;
  218. }
  219. ULONG
  220. ReflectorSendSimpleFsControl(
  221. PUMRX_USERMODE_REFLECT_BLOCK Reflector,
  222. ULONG IoctlCode
  223. )
  224. /*++
  225. Routine Description:
  226. This sends an FSCTL to the device object associated with the Reflector
  227. block.
  228. Arguments:
  229. Relector - The datastructure associated which was returned to the usermode
  230. process at initialization time.
  231. IoctlCode - The FsCtl code for the operation.
  232. Return Value:
  233. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  234. success.
  235. --*/
  236. {
  237. ULONG rc;
  238. IO_STATUS_BLOCK IoStatusBlock;
  239. if (Reflector == NULL) {
  240. rc = ERROR_INVALID_PARAMETER;
  241. return rc;
  242. }
  243. //
  244. // Send the FSCTL to the Mini-Redir.
  245. //
  246. if (Reflector->DeviceHandle != INVALID_HANDLE_VALUE) {
  247. rc = NtFsControlFile(Reflector->DeviceHandle,
  248. 0,
  249. NULL,
  250. NULL,
  251. &IoStatusBlock,
  252. IoctlCode,
  253. NULL,
  254. 0,
  255. NULL,
  256. 0);
  257. } else {
  258. rc = ERROR_OPEN_FAILED;
  259. }
  260. return rc;
  261. }
  262. ULONG
  263. UMReflectorStart(
  264. ULONG ReflectorVersion,
  265. PUMRX_USERMODE_REFLECT_BLOCK Reflector
  266. )
  267. /*++
  268. Routine Description:
  269. This routine sends an FSCTL to start the Mini-Redir. Before we send the
  270. Fsctl, we find out the path to the WinInet cache on the local machine. We
  271. then send this down to the kernel via the Fsctl. The Dav MiniRedir stores
  272. the value of this path in a global variable and uses it to answer any volume
  273. information queries.
  274. Arguments:
  275. ReflectorVersion - The reflector's version.
  276. Handle - The handle created by the reflector library.
  277. Return Value:
  278. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  279. success.
  280. --*/
  281. {
  282. ULONG WStatus = ERROR_SUCCESS;
  283. PDAV_USERMODE_DATA DavUserModeData = NULL;
  284. PFN_GETWININET_CACHE_PATH pfnSHGetSpecialFolderPath;
  285. HMODULE hShell32 = NULL;
  286. BOOL ReturnVal;
  287. IO_STATUS_BLOCK IoStatusBlock;
  288. if (ReflectorVersion != UMREFLECTOR_CURRENT_VERSION) {
  289. //
  290. // Whoops. Mismatch here. We should support backward levels but right
  291. // now there aren't any so we just bail.
  292. //
  293. return ERROR_NOT_SUPPORTED;
  294. }
  295. if (Reflector == NULL) {
  296. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart. Reflector == NULL\n",
  297. GetCurrentThreadId()));
  298. WStatus = ERROR_INVALID_PARAMETER;
  299. goto EXIT_THE_FUNCTION;
  300. }
  301. DavUserModeData = LocalAlloc( (LMEM_FIXED | LMEM_ZEROINIT), sizeof(DAV_USERMODE_DATA));
  302. if (DavUserModeData == NULL) {
  303. WStatus = GetLastError();
  304. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart/LocalAlloc. WStatus = %d\n",
  305. GetCurrentThreadId(), WStatus));
  306. goto EXIT_THE_FUNCTION;
  307. }
  308. //
  309. // Get the Path of the WinInet cache. To do this we need to load shell32.dll,
  310. // get the address of the function SHGetSpecialFolderPath and call it with
  311. // CSIDL_INTERNET_CACHE.
  312. //
  313. //
  314. // Store the Pid of the process.
  315. //
  316. DavUserModeData->ProcessId = GetCurrentProcessId();
  317. hShell32 = LoadLibraryW(L"shell32.dll");
  318. if (hShell32 == NULL) {
  319. WStatus = GetLastError();
  320. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart/LoadLibrary:"
  321. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  322. goto EXIT_THE_FUNCTION;
  323. }
  324. pfnSHGetSpecialFolderPath = (PFN_GETWININET_CACHE_PATH)
  325. GetProcAddress(hShell32,
  326. "SHGetSpecialFolderPathW");
  327. if (pfnSHGetSpecialFolderPath == NULL) {
  328. WStatus = GetLastError();
  329. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart/GetProcAddress:"
  330. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  331. goto EXIT_THE_FUNCTION;
  332. }
  333. ReturnVal = pfnSHGetSpecialFolderPath(NULL,
  334. (LPWSTR)DavUserModeData->WinInetCachePath,
  335. CSIDL_INTERNET_CACHE,
  336. FALSE);
  337. if (!ReturnVal) {
  338. WStatus = ERROR_INVALID_PARAMETER;
  339. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart/pfnSHGetSpecialFolderPath:"
  340. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  341. goto EXIT_THE_FUNCTION;
  342. }
  343. //
  344. // Now issue an FSCTL down to the MiniRedir.
  345. //
  346. if (Reflector->DeviceHandle != INVALID_HANDLE_VALUE) {
  347. WStatus = NtFsControlFile(Reflector->DeviceHandle,
  348. 0,
  349. NULL,
  350. NULL,
  351. &IoStatusBlock,
  352. FSCTL_UMRX_START,
  353. DavUserModeData,
  354. sizeof(DAV_USERMODE_DATA),
  355. NULL,
  356. 0);
  357. if (WStatus != ERROR_SUCCESS) {
  358. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart/NtFsControlFile:"
  359. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  360. goto EXIT_THE_FUNCTION;
  361. }
  362. } else {
  363. WStatus = ERROR_OPEN_FAILED;
  364. RlDavDbgPrint(("%ld: ERROR: UMReflectorStart. DeviceHandle == INVALID_HANDLE_VALUE\n",
  365. GetCurrentThreadId()));
  366. goto EXIT_THE_FUNCTION;
  367. }
  368. EXIT_THE_FUNCTION:
  369. if (DavUserModeData) {
  370. LocalFree(DavUserModeData);
  371. }
  372. return WStatus;
  373. }
  374. ULONG
  375. UMReflectorStop(
  376. PUMRX_USERMODE_REFLECT_BLOCK Reflector
  377. )
  378. /*++
  379. Routine Description:
  380. This routine sends an FSCTL to stop the Mini-Redir.
  381. Arguments:
  382. ReflectorVersion - The reflector's version.
  383. Handle - The handle created by the reflector library.
  384. Return Value:
  385. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  386. success.
  387. --*/
  388. {
  389. return ReflectorSendSimpleFsControl(Reflector, FSCTL_UMRX_STOP);
  390. }
  391. ULONG
  392. UMReflectorReleaseThreads (
  393. PUMRX_USERMODE_REFLECT_BLOCK Reflector
  394. )
  395. /*++
  396. Routine Description:
  397. If any user mode threads are waiting for requests, they'll return
  398. immediately.
  399. Arguments:
  400. Handle - The handle created by the reflector library.
  401. Return Value:
  402. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  403. success.
  404. --*/
  405. {
  406. IO_STATUS_BLOCK IoStatusBlock;
  407. OVERLAPPED OverLapped;
  408. BOOL SuccessfulOperation;
  409. ULONG rc = ERROR_SUCCESS;
  410. if (Reflector == NULL) {
  411. return ERROR_INVALID_PARAMETER;
  412. }
  413. if (Reflector->DeviceHandle != INVALID_HANDLE_VALUE) {
  414. RtlZeroMemory(&OverLapped, sizeof(OverLapped));
  415. SuccessfulOperation = DeviceIoControl(Reflector->DeviceHandle,
  416. IOCTL_UMRX_RELEASE_THREADS,
  417. NULL,
  418. 0,
  419. NULL,
  420. 0,
  421. NULL,
  422. &OverLapped);
  423. if (!SuccessfulOperation) {
  424. rc = GetLastError();
  425. }
  426. }
  427. return rc;
  428. }
  429. ULONG
  430. UMReflectorOpenWorker(
  431. IN PUMRX_USERMODE_REFLECT_BLOCK Reflector,
  432. OUT PUMRX_USERMODE_WORKER_INSTANCE *WorkerHandle
  433. )
  434. /*++
  435. Routine Description:
  436. This allocates a "per worker thread" structure for the app so that it can
  437. have multiple IOCTLs pending down into kernel on different threads. If
  438. we just open them up asynchronous, then we don't use the fast path. If
  439. we open them up synchronous and use the same handle, then only one thread
  440. gets past the I/O manager at any given time.
  441. Arguments:
  442. Reflector - The reflector block allocated for the Mini-Redir.
  443. WorkerHandle - The worker handle that is created and returned.
  444. Return Value:
  445. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  446. success.
  447. --*/
  448. {
  449. ULONG rc = STATUS_SUCCESS;
  450. PUMRX_USERMODE_WORKER_INSTANCE worker;
  451. IO_STATUS_BLOCK IoStatusBlock;
  452. OBJECT_ATTRIBUTES ObjectAttributes;
  453. UNICODE_STRING DeviceObjectName;
  454. worker = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  455. sizeof(UMRX_USERMODE_WORKER_INSTANCE));
  456. *WorkerHandle = worker;
  457. if (worker == NULL) {
  458. rc = ERROR_NOT_ENOUGH_MEMORY;
  459. goto errorExit;
  460. }
  461. worker->ReflectorInstance = Reflector;
  462. EnterCriticalSection( &(Reflector->Lock) );
  463. RtlInitUnicodeString(&DeviceObjectName, Reflector->DriverDeviceName);
  464. InitializeObjectAttributes(&ObjectAttributes,
  465. &DeviceObjectName,
  466. OBJ_CASE_INSENSITIVE,
  467. NULL,
  468. NULL);
  469. rc = NtOpenFile(&worker->ReflectorHandle,
  470. SYNCHRONIZE,
  471. &ObjectAttributes,
  472. &IoStatusBlock,
  473. FILE_SHARE_VALID_FLAGS,
  474. FILE_SYNCHRONOUS_IO_ALERT);
  475. if (rc != STATUS_SUCCESS) {
  476. LeaveCriticalSection(&Reflector->Lock);
  477. rc = RtlNtStatusToDosError(rc);
  478. goto errorExit;
  479. }
  480. //
  481. // Now we just add it to the list and we're done.
  482. //
  483. Reflector->ReferenceCount++;
  484. InsertTailList(&Reflector->WorkerList, &worker->WorkerListEntry);
  485. LeaveCriticalSection( &(Reflector->Lock) );
  486. errorExit:
  487. if (rc != STATUS_SUCCESS) {
  488. //
  489. // Things failed here. Let's clean up.
  490. //
  491. if (worker != NULL) {
  492. LocalFree(worker);
  493. }
  494. *WorkerHandle = NULL;
  495. }
  496. return rc;
  497. }
  498. VOID
  499. UMReflectorCloseWorker(
  500. PUMRX_USERMODE_WORKER_INSTANCE Worker
  501. )
  502. /*++
  503. Routine Description:
  504. This routine finalizes a worker structure.
  505. Arguments:
  506. Worker - The worker structure for this thread.
  507. Return Value:
  508. The return value is a Win32 error code. STATUS_SUCCESS is returned on
  509. success.
  510. --*/
  511. {
  512. EnterCriticalSection( &(Worker->ReflectorInstance->Lock) );
  513. if (Worker->ReflectorHandle != INVALID_HANDLE_VALUE) {
  514. NtClose( Worker->ReflectorHandle );
  515. Worker->ReflectorHandle = INVALID_HANDLE_VALUE;
  516. }
  517. RemoveEntryList(&Worker->WorkerListEntry);
  518. DereferenceReflectorBlock(Worker->ReflectorInstance);
  519. LocalFree(Worker);
  520. return;
  521. }
  522. VOID
  523. UMReflectorCompleteRequest(
  524. PUMRX_USERMODE_REFLECT_BLOCK ReflectorHandle,
  525. PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader
  526. )
  527. /*++
  528. Routine Description:
  529. This routine completes an async request being handled by an async queue
  530. thread. These threads should not be confused with the worker threads that
  531. are spun by the DAV user mode process to reflect requests. This will just
  532. send a response down and come back.
  533. Arguments:
  534. ReflectorHandle - Address of the Reflector block strucutre for this process.
  535. WorkItemHeader - The user mode work item header.
  536. Return Value:
  537. none.
  538. --*/
  539. {
  540. ULONG WStatus = ERROR_SUCCESS;
  541. PUMRX_USERMODE_WORKER_INSTANCE WorkerHandle = NULL;
  542. //
  543. // Get a worker instance for this thread.
  544. //
  545. WStatus = UMReflectorOpenWorker(ReflectorHandle, &WorkerHandle);
  546. if (WStatus != ERROR_SUCCESS || WorkerHandle == NULL) {
  547. if (WStatus == ERROR_SUCCESS) {
  548. WStatus = ERROR_INTERNAL_ERROR;
  549. }
  550. RlDavDbgPrint(("%ld: ERROR: UMReflectorCompleteRequest/UMReflectorOpenWorker:"
  551. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  552. goto EXIT_THE_FUNCTION;
  553. }
  554. //
  555. // Send the response.
  556. //
  557. WStatus = UMReflectorSendResponse(WorkerHandle, WorkItemHeader);
  558. if (WStatus != ERROR_SUCCESS) {
  559. RlDavDbgPrint(("%ld: ERROR: UMReflectorCompleteRequest/UMReflectorSendResponse:"
  560. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  561. }
  562. //
  563. // If the request got cancelled in the kernelmode and we need to do some
  564. // cleanup, then the callWorkItemCleanup flag will be set to TRUE by the
  565. // Precomplete routine in the kernel. If it is TRUE then we call the cleanup
  566. // routine.
  567. //
  568. if (WorkItemHeader->callWorkItemCleanup) {
  569. DavCleanupWorkItem(WorkItemHeader);
  570. }
  571. //
  572. // Complete the work item.
  573. //
  574. WStatus = UMReflectorCompleteWorkItem(WorkerHandle, WorkItemHeader);
  575. if (WStatus != ERROR_SUCCESS) {
  576. RlDavDbgPrint(("%ld: ERROR: UMReflectorCompleteRequest/UMReflectorCompleteWorkItem:"
  577. " WStatus = %08lx.\n", GetCurrentThreadId(), WStatus));
  578. }
  579. EXIT_THE_FUNCTION:
  580. //
  581. // Free the worker instance now, since our job is done.
  582. //
  583. if (WorkerHandle) {
  584. UMReflectorCloseWorker(WorkerHandle);
  585. }
  586. return;
  587. }