Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenclose.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS FAT file system driver 00003 * LICENSE: GNU GPLv3 as published by the Free Software Foundation 00004 * FILE: drivers/filesystems/fastfat/close.c 00005 * PURPOSE: Closing routines 00006 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) 00007 */ 00008 00009 /* INCLUDES *****************************************************************/ 00010 00011 #define NDEBUG 00012 #include "fastfat.h" 00013 00014 VOID NTAPI 00015 FatQueueClose(IN PCLOSE_CONTEXT CloseContext, 00016 IN BOOLEAN DelayClose); 00017 00018 PCLOSE_CONTEXT NTAPI 00019 FatRemoveClose(PVCB Vcb OPTIONAL, 00020 PVCB LastVcbHint OPTIONAL); 00021 00022 const ULONG FatMaxDelayedCloseCount = 16; 00023 00024 /* FUNCTIONS ****************************************************************/ 00025 00026 NTSTATUS 00027 NTAPI 00028 FatiCommonClose(IN PVCB Vcb, 00029 IN PFCB Fcb, 00030 IN PCCB Ccb, 00031 IN TYPE_OF_OPEN TypeOfOpen, 00032 IN BOOLEAN Wait, 00033 OUT PBOOLEAN VcbDeleted) 00034 { 00035 NTSTATUS Status; 00036 PFCB ParentDcb; 00037 BOOLEAN RecursiveClose, VcbDeletedLv = FALSE; 00038 FAT_IRP_CONTEXT IrpContext; 00039 00040 if (VcbDeleted) *VcbDeleted = FALSE; 00041 00042 if (TypeOfOpen == UnopenedFileObject) 00043 { 00044 DPRINT1("Closing unopened file object\n"); 00045 Status = STATUS_SUCCESS; 00046 return Status; 00047 } 00048 00049 RtlZeroMemory(&IrpContext, sizeof(FAT_IRP_CONTEXT)); 00050 00051 IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; 00052 IrpContext.NodeByteSize = sizeof(IrpContext); 00053 IrpContext.MajorFunction = IRP_MJ_CLOSE; 00054 00055 if (Wait) SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT); 00056 00057 if (!ExAcquireResourceExclusiveLite(&Vcb->Resource, Wait)) return STATUS_PENDING; 00058 00059 if (Vcb->State & VCB_STATE_FLAG_CLOSE_IN_PROGRESS) 00060 { 00061 RecursiveClose = TRUE; 00062 } 00063 else 00064 { 00065 SetFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); 00066 RecursiveClose = FALSE; 00067 00068 Vcb->OpenFileCount++; 00069 } 00070 00071 /* Update on-disk structures */ 00072 switch (TypeOfOpen) 00073 { 00074 case VirtualVolumeFile: 00075 DPRINT1("Close VirtualVolumeFile\n"); 00076 00077 InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount)); 00078 InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount)); 00079 00080 Status = STATUS_SUCCESS; 00081 goto close_done; 00082 break; 00083 00084 case UserVolumeOpen: 00085 DPRINT1("Close UserVolumeOpen\n"); 00086 00087 Vcb->DirectAccessOpenCount--; 00088 Vcb->OpenFileCount--; 00089 if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount--; 00090 00091 FatDeleteCcb(&IrpContext, Ccb); 00092 00093 Status = STATUS_SUCCESS; 00094 goto close_done; 00095 break; 00096 00097 case EaFile: 00098 UNIMPLEMENTED; 00099 break; 00100 00101 case DirectoryFile: 00102 DPRINT1("Close DirectoryFile\n"); 00103 00104 InterlockedDecrement((PLONG)&(Fcb->Dcb.DirectoryFileOpenCount)); 00105 InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount)); 00106 00107 if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB) 00108 { 00109 InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount)); 00110 } 00111 00112 if (RecursiveClose) 00113 { 00114 Status = STATUS_SUCCESS; 00115 goto close_done; 00116 } 00117 else 00118 { 00119 break; 00120 } 00121 00122 case UserDirectoryOpen: 00123 case UserFileOpen: 00124 DPRINT("Close UserFileOpen/UserDirectoryOpen\n"); 00125 00126 if ((FatNodeType(Fcb) == FAT_NTC_DCB) && 00127 IsListEmpty(&Fcb->Dcb.ParentDcbList) && 00128 (Fcb->OpenCount == 1) && 00129 (Fcb->Dcb.DirectoryFile != NULL)) 00130 { 00131 PFILE_OBJECT DirectoryFileObject = Fcb->Dcb.DirectoryFile; 00132 00133 DPRINT1("Uninitialize the stream file object\n"); 00134 00135 CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL); 00136 00137 Fcb->Dcb.DirectoryFile = NULL; 00138 ObDereferenceObject(DirectoryFileObject); 00139 } 00140 00141 Fcb->OpenCount--; 00142 Vcb->OpenFileCount--; 00143 if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount --; 00144 00145 FatDeleteCcb(&IrpContext, Ccb); 00146 break; 00147 00148 default: 00149 KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)TypeOfOpen, 0, 0); 00150 } 00151 00152 /* Update in-memory structures */ 00153 if (((FatNodeType(Fcb) == FAT_NTC_FCB) && 00154 (Fcb->OpenCount == 0)) 00155 || 00156 ((FatNodeType(Fcb) == FAT_NTC_DCB) && 00157 (IsListEmpty(&Fcb->Dcb.ParentDcbList)) && 00158 (Fcb->OpenCount == 0) && 00159 (Fcb->Dcb.DirectoryFileOpenCount == 0))) 00160 { 00161 ParentDcb = Fcb->ParentFcb; 00162 00163 SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB); 00164 00165 FatDeleteFcb(&IrpContext, Fcb); 00166 00167 while ((FatNodeType(ParentDcb) == FAT_NTC_DCB) && 00168 IsListEmpty(&ParentDcb->Dcb.ParentDcbList) && 00169 (ParentDcb->OpenCount == 0) && 00170 (ParentDcb->Dcb.DirectoryFile != NULL)) 00171 { 00172 PFILE_OBJECT DirectoryFileObject; 00173 00174 DirectoryFileObject = ParentDcb->Dcb.DirectoryFile; 00175 00176 DPRINT1("Uninitialize parent Stream Cache Map\n"); 00177 00178 CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL); 00179 00180 ParentDcb->Dcb.DirectoryFile = NULL; 00181 00182 ObDereferenceObject(DirectoryFileObject); 00183 00184 if (ParentDcb->Dcb.DirectoryFileOpenCount == 0) 00185 { 00186 PFCB CurrentDcb; 00187 00188 CurrentDcb = ParentDcb; 00189 ParentDcb = CurrentDcb->ParentFcb; 00190 00191 SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB); 00192 00193 FatDeleteFcb(&IrpContext, CurrentDcb); 00194 } 00195 else 00196 { 00197 break; 00198 } 00199 } 00200 } 00201 00202 Status = STATUS_SUCCESS; 00203 00204 close_done: 00205 /* Closing is done, check if VCB could be closed too */ 00206 if (!RecursiveClose) 00207 { 00208 /* One open left - yes, VCB can go away */ 00209 if (Vcb->OpenFileCount == 1 && 00210 !FlagOn(Vcb->State, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS) 00211 && VcbDeleted) 00212 { 00213 FatReleaseVcb(&IrpContext, Vcb ); 00214 00215 SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT); 00216 00217 FatAcquireExclusiveGlobal(&IrpContext); 00218 00219 FatAcquireExclusiveVcb(&IrpContext, Vcb); 00220 00221 Vcb->OpenFileCount--; 00222 00223 VcbDeletedLv = FatCheckForDismount(&IrpContext, Vcb, FALSE); 00224 00225 FatReleaseGlobal(&IrpContext); 00226 00227 if (VcbDeleted) *VcbDeleted = VcbDeletedLv; 00228 } 00229 else 00230 { 00231 /* Remove extra referenec */ 00232 Vcb->OpenFileCount --; 00233 } 00234 00235 /* Clear recursion flag if necessary */ 00236 if (!VcbDeletedLv) 00237 { 00238 ClearFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); 00239 } 00240 } 00241 00242 /* Release VCB if it wasn't deleted */ 00243 if (!VcbDeletedLv) 00244 FatReleaseVcb(&IrpContext, Vcb); 00245 00246 return Status; 00247 } 00248 00249 NTSTATUS 00250 NTAPI 00251 FatiClose(IN PFAT_IRP_CONTEXT IrpContext, 00252 IN PIRP Irp) 00253 { 00254 PIO_STACK_LOCATION IrpSp; 00255 TYPE_OF_OPEN TypeOfOpen; 00256 PVCB Vcb; 00257 PFCB Fcb; 00258 PCCB Ccb; 00259 BOOLEAN TopLevel, Wait, VcbDeleted = FALSE, DelayedClose = FALSE; 00260 NTSTATUS Status = STATUS_SUCCESS; 00261 PCLOSE_CONTEXT CloseContext = NULL; 00262 00263 TopLevel = FatIsTopLevelIrp(Irp); 00264 00265 /* Get current IRP stack location */ 00266 IrpSp = IoGetCurrentIrpStackLocation(Irp); 00267 00268 /* Decode incoming file object */ 00269 TypeOfOpen = FatDecodeFileObject(IrpSp->FileObject, &Vcb, &Fcb, &Ccb); 00270 00271 /* Set CCB read only flag */ 00272 if (Ccb && IsFileObjectReadOnly(IrpSp->FileObject)) 00273 SetFlag(Ccb->Flags, CCB_READ_ONLY); 00274 00275 /* It's possible to wait only if we are top level or not a system process */ 00276 Wait = TopLevel && (PsGetCurrentProcess() != FatGlobalData.SystemProcess); 00277 00278 /* Determine if it's a delayed close, by flags first */ 00279 if ((TypeOfOpen == UserFileOpen || TypeOfOpen == UserDirectoryOpen) && 00280 (Fcb->State & FCB_STATE_DELAY_CLOSE) && 00281 !FatGlobalData.ShutdownStarted) 00282 { 00283 DelayedClose = TRUE; 00284 } 00285 00286 /* If close is not delayed, try to perform the close operation */ 00287 if (!DelayedClose) 00288 Status = FatiCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted); 00289 00290 /* We have to delay close if either it's defined by a flag or it was not possible 00291 to perform it synchronously */ 00292 if (DelayedClose || Status == STATUS_PENDING) 00293 { 00294 DPRINT1("Queuing a pending close, Vcb %p, Fcb %p, Ccb %p\n", Vcb, Fcb, Ccb); 00295 00296 /* Check if a close context should be allocated */ 00297 if (TypeOfOpen == VirtualVolumeFile) 00298 { 00299 ASSERT(Vcb->CloseContext != NULL); 00300 CloseContext = Vcb->CloseContext; 00301 Vcb->CloseContext = NULL; 00302 CloseContext->Free = TRUE; 00303 } 00304 else if (TypeOfOpen == DirectoryFile || 00305 TypeOfOpen == EaFile) 00306 { 00307 UNIMPLEMENTED; 00308 //CloseContext = FatAllocateCloseContext(Vcb); 00309 //ASSERT(CloseContext != NULL); 00310 CloseContext->Free = TRUE; 00311 } 00312 else 00313 { 00314 //TODO: FatDeallocateCcbStrings( Ccb ); 00315 00316 /* Set CloseContext to a buffer inside Ccb */ 00317 CloseContext = &Ccb->CloseContext; 00318 CloseContext->Free = FALSE; 00319 SetFlag(Ccb->Flags, CCB_CLOSE_CONTEXT); 00320 } 00321 00322 /* Save all info in the close context */ 00323 CloseContext->Vcb = Vcb; 00324 CloseContext->Fcb = Fcb; 00325 CloseContext->TypeOfOpen = TypeOfOpen; 00326 00327 /* Queue the close */ 00328 FatQueueClose(CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->State, FCB_STATE_DELAY_CLOSE))); 00329 } 00330 else 00331 { 00332 /* Close finished right away */ 00333 if (TypeOfOpen == VirtualVolumeFile || 00334 TypeOfOpen == DirectoryFile || 00335 TypeOfOpen == EaFile) 00336 { 00337 if (TypeOfOpen == VirtualVolumeFile) 00338 { 00339 /* Free close context for the not deleted VCB */ 00340 if (!VcbDeleted) 00341 { 00342 CloseContext = Vcb->CloseContext; 00343 Vcb->CloseContext = NULL; 00344 00345 ASSERT(CloseContext != NULL); 00346 } 00347 } 00348 else 00349 { 00350 //CloseContext = FatAllocateCloseContext(Vcb); 00351 DPRINT1("TODO: Allocate close context!\n"); 00352 ASSERT(CloseContext != NULL); 00353 } 00354 00355 /* Free close context */ 00356 if (CloseContext) ExFreePool(CloseContext); 00357 } 00358 } 00359 00360 /* Complete the request */ 00361 FatCompleteRequest(NULL, Irp, Status); 00362 00363 /* Reset the top level IRP if necessary */ 00364 if (TopLevel) IoSetTopLevelIrp(NULL); 00365 00366 return Status; 00367 } 00368 00369 NTSTATUS 00370 NTAPI 00371 FatClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) 00372 { 00373 PFAT_IRP_CONTEXT IrpContext; 00374 NTSTATUS Status; 00375 00376 DPRINT("FatClose(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); 00377 00378 /* FatClose works only with a volume device object */ 00379 if (DeviceObject == FatGlobalData.DiskDeviceObject) 00380 { 00381 /* Complete the request and return success */ 00382 Irp->IoStatus.Status = STATUS_SUCCESS; 00383 Irp->IoStatus.Information = FILE_OPENED; 00384 00385 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 00386 00387 return STATUS_SUCCESS; 00388 } 00389 00390 /* Enter FsRtl critical region */ 00391 FsRtlEnterFileSystem(); 00392 00393 /* Build an irp context */ 00394 IrpContext = FatBuildIrpContext(Irp, TRUE); 00395 00396 /* Call internal function */ 00397 Status = FatiClose(IrpContext, Irp); 00398 00399 /* Leave FsRtl critical region */ 00400 FsRtlExitFileSystem(); 00401 00402 return Status; 00403 } 00404 00405 VOID 00406 NTAPI 00407 FatPendingClose(IN PVCB Vcb OPTIONAL) 00408 { 00409 PCLOSE_CONTEXT CloseContext; 00410 PVCB CurrentVcb = NULL; 00411 PVCB LastVcb = NULL; 00412 BOOLEAN FreeContext; 00413 ULONG Loops = 0; 00414 00415 /* Do the top-level IRP trick */ 00416 if (!Vcb) IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); 00417 00418 while ((CloseContext = FatRemoveClose(Vcb, LastVcb))) 00419 { 00420 if (!Vcb) 00421 { 00422 if (!FatGlobalData.ShutdownStarted) 00423 { 00424 if (CloseContext->Vcb != CurrentVcb) 00425 { 00426 Loops = 0; 00427 00428 /* Release previous VCB */ 00429 if (CurrentVcb) 00430 ExReleaseResourceLite(&CurrentVcb->Resource); 00431 00432 /* Lock the new VCB */ 00433 CurrentVcb = CloseContext->Vcb; 00434 (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE); 00435 } 00436 else 00437 { 00438 /* Try to lock */ 00439 if (++Loops >= 20) 00440 { 00441 if (ExGetSharedWaiterCount(&CurrentVcb->Resource) + 00442 ExGetExclusiveWaiterCount(&CurrentVcb->Resource)) 00443 { 00444 ExReleaseResourceLite(&CurrentVcb->Resource); 00445 (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE); 00446 } 00447 00448 Loops = 0; 00449 } 00450 } 00451 00452 /* Check open count */ 00453 if (CurrentVcb->OpenFileCount <= 1) 00454 { 00455 ExReleaseResourceLite(&CurrentVcb->Resource); 00456 CurrentVcb = NULL; 00457 } 00458 } 00459 else if (CurrentVcb) 00460 { 00461 ExReleaseResourceLite(&CurrentVcb->Resource); 00462 CurrentVcb = NULL; 00463 } 00464 } 00465 00466 LastVcb = CurrentVcb; 00467 00468 /* Remember if we should free the context */ 00469 FreeContext = CloseContext->Free; 00470 00471 FatiCommonClose(CloseContext->Vcb, 00472 CloseContext->Fcb, 00473 (FreeContext ? NULL : CONTAINING_RECORD(CloseContext, CCB, CloseContext)), 00474 CloseContext->TypeOfOpen, 00475 TRUE, 00476 NULL); 00477 00478 /* Free context if necessary */ 00479 if (FreeContext) ExFreePool(CloseContext); 00480 } 00481 00482 /* Release VCB if necessary */ 00483 if (CurrentVcb) ExReleaseResourceLite(&CurrentVcb->Resource); 00484 00485 /* Reset top level IRP */ 00486 if (!Vcb) IoSetTopLevelIrp( NULL ); 00487 } 00488 00489 VOID 00490 NTAPI 00491 FatCloseWorker(IN PDEVICE_OBJECT DeviceObject, 00492 IN PVOID Context) 00493 { 00494 FsRtlEnterFileSystem(); 00495 00496 FatPendingClose((PVCB)Context); 00497 00498 FsRtlExitFileSystem(); 00499 } 00500 00501 VOID 00502 NTAPI 00503 FatQueueClose(IN PCLOSE_CONTEXT CloseContext, 00504 IN BOOLEAN DelayClose) 00505 { 00506 BOOLEAN RunWorker = FALSE; 00507 00508 /* Acquire the close lists mutex */ 00509 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex); 00510 00511 /* Add it to the desired list */ 00512 if (DelayClose) 00513 { 00514 InsertTailList(&FatGlobalData.DelayedCloseList, 00515 &CloseContext->GlobalLinks); 00516 InsertTailList(&CloseContext->Vcb->DelayedCloseList, 00517 &CloseContext->VcbLinks); 00518 00519 FatGlobalData.DelayedCloseCount++; 00520 00521 if (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount && 00522 !FatGlobalData.AsyncCloseActive) 00523 { 00524 FatGlobalData.AsyncCloseActive = TRUE; 00525 RunWorker = TRUE; 00526 } 00527 } 00528 else 00529 { 00530 InsertTailList(&FatGlobalData.AsyncCloseList, 00531 &CloseContext->GlobalLinks); 00532 InsertTailList(&CloseContext->Vcb->AsyncCloseList, 00533 &CloseContext->VcbLinks); 00534 00535 FatGlobalData.AsyncCloseCount++; 00536 00537 if (!FatGlobalData.AsyncCloseActive) 00538 { 00539 FatGlobalData.AsyncCloseActive = TRUE; 00540 RunWorker = TRUE; 00541 } 00542 } 00543 00544 /* Release the close lists mutex */ 00545 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex); 00546 00547 if (RunWorker) 00548 IoQueueWorkItem(FatGlobalData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL); 00549 } 00550 00551 PCLOSE_CONTEXT 00552 NTAPI 00553 FatRemoveClose(PVCB Vcb OPTIONAL, 00554 PVCB LastVcbHint OPTIONAL) 00555 { 00556 PLIST_ENTRY Entry; 00557 PCLOSE_CONTEXT CloseContext; 00558 BOOLEAN IsWorker = FALSE; 00559 00560 /* Acquire the close lists mutex */ 00561 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex); 00562 00563 if (!Vcb) IsWorker = TRUE; 00564 00565 if (Vcb == NULL && LastVcbHint != NULL) 00566 { 00567 // TODO: A very special case of overflowing the queue 00568 UNIMPLEMENTED; 00569 } 00570 00571 /* Usual processing from a worker thread */ 00572 if (!Vcb) 00573 { 00574 TryToCloseAgain: 00575 00576 /* Is there anything in the async close list */ 00577 if (!IsListEmpty(&FatGlobalData.AsyncCloseList)) 00578 { 00579 Entry = RemoveHeadList(&FatGlobalData.AsyncCloseList); 00580 FatGlobalData.AsyncCloseCount--; 00581 00582 CloseContext = CONTAINING_RECORD(Entry, 00583 CLOSE_CONTEXT, 00584 GlobalLinks); 00585 00586 RemoveEntryList(&CloseContext->VcbLinks); 00587 } else if (!IsListEmpty(&FatGlobalData.DelayedCloseList) && 00588 (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount/2 || 00589 FatGlobalData.ShutdownStarted)) 00590 { 00591 /* In case of a shutdown or when delayed queue is filled at half - perform closing */ 00592 Entry = RemoveHeadList(&FatGlobalData.DelayedCloseList); 00593 FatGlobalData.DelayedCloseCount--; 00594 00595 CloseContext = CONTAINING_RECORD(Entry, 00596 CLOSE_CONTEXT, 00597 GlobalLinks); 00598 RemoveEntryList(&CloseContext->VcbLinks); 00599 } 00600 else 00601 { 00602 /* Nothing to close */ 00603 CloseContext = NULL; 00604 if (IsWorker) FatGlobalData.AsyncCloseActive = FALSE; 00605 } 00606 } 00607 else 00608 { 00609 if (!IsListEmpty(&Vcb->AsyncCloseList)) 00610 { 00611 /* Is there anything in the async close list */ 00612 Entry = RemoveHeadList(&Vcb->AsyncCloseList); 00613 FatGlobalData.AsyncCloseCount--; 00614 00615 CloseContext = CONTAINING_RECORD(Entry, 00616 CLOSE_CONTEXT, 00617 VcbLinks); 00618 00619 RemoveEntryList(&CloseContext->GlobalLinks); 00620 } 00621 else if (!IsListEmpty(&Vcb->DelayedCloseList)) 00622 { 00623 /* Process delayed close list */ 00624 Entry = RemoveHeadList(&Vcb->DelayedCloseList); 00625 FatGlobalData.DelayedCloseCount--; 00626 00627 CloseContext = CONTAINING_RECORD(Entry, 00628 CLOSE_CONTEXT, 00629 VcbLinks); 00630 00631 RemoveEntryList(&CloseContext->GlobalLinks); 00632 } 00633 else if (LastVcbHint) 00634 { 00635 /* Try again */ 00636 goto TryToCloseAgain; 00637 } 00638 else 00639 { 00640 /* Nothing to close */ 00641 CloseContext = NULL; 00642 } 00643 } 00644 00645 /* Release the close lists mutex */ 00646 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex); 00647 00648 return CloseContext; 00649 } 00650 00651 /* EOF */ Generated on Sun May 27 2012 04:27:34 for ReactOS by
1.7.6.1
|