Index: CMakeLists.txt =================================================================== --- CMakeLists.txt (revision 62485) +++ CMakeLists.txt (working copy) @@ -38,6 +38,7 @@ ntos_ex/ExSingleList.c ntos_ex/ExTimer.c ntos_fsrtl/FsRtlExpression.c + ntos_fsrtl/FsRtlLockFile.c ntos_fsrtl/FsRtlMcb.c ntos_fsrtl/FsRtlTunnel.c ntos_io/IoDeviceInterface.c Index: kmtest_drv/testlist.c =================================================================== --- kmtest_drv/testlist.c (revision 62485) +++ kmtest_drv/testlist.c (working copy) @@ -20,6 +20,7 @@ KMT_TESTFUNC Test_ExSingleList; KMT_TESTFUNC Test_ExTimer; KMT_TESTFUNC Test_FsRtlExpression; +KMT_TESTFUNC Test_FsRtlLockFile; KMT_TESTFUNC Test_FsRtlMcb; KMT_TESTFUNC Test_FsRtlTunnel; KMT_TESTFUNC Test_IoDeviceInterface; @@ -70,6 +71,7 @@ { "-ExTimer", Test_ExTimer }, { "Example", Test_Example }, { "FsRtlExpression", Test_FsRtlExpression }, + { "FsRtlLockFile", Test_FsRtlLockFile}, /* Skipped on testman. See ROSTESTS-106. */ { "-FsRtlMcb", Test_FsRtlMcb }, { "-FsRtlTunnel", Test_FsRtlTunnel }, Index: ntos_fsrtl/FsRtlLockFile.c =================================================================== --- ntos_fsrtl/FsRtlLockFile.c (revision 0) +++ ntos_fsrtl/FsRtlLockFile.c (working copy) @@ -0,0 +1,451 @@ +/* +* PROJECT: ReactOS kernel-mode tests +* LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory +* PURPOSE: Kernel-Mode Test Suite FsRtl Test +* PROGRAMMER: Lesan Ilie +*/ + +#include + +#define NDEBUG +#include + +#define TEST_NO 6 +#define NO_KEY 0x00000000 + + HANDLE GlobalHandle; + CCHAR FileName[32] = "\\DosDevices\\c:\\lock.txt"; + CCHAR WriteBuffer[100] = "TakingANewStep,UtteringANewWord,IsWhatPeopleFearMost. ArrivingAtOneGoalIsTheStartingPointToAnother."; + UNICODE_STRING UnicodeFileName; + STRING StringName; + IO_STATUS_BLOCK IoStatus; + OBJECT_ATTRIBUTES Attributes; + NTSTATUS Status; + +typedef struct _TEST_DATA +{ + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; +}TEST_DATA, *PTEST_DATA; + +typedef struct _FILELOCK_DATA +{ + LARGE_INTEGER FileOffset[TEST_NO]; + LARGE_INTEGER Length[TEST_NO]; + TEST_DATA TestData[TEST_NO]; + PFILE_LOCK FileLock; + PFILE_LOCK_INFO LockInfo; + PFILE_OBJECT FileObject; + PEPROCESS ProcessId; + IO_STATUS_BLOCK IoStatus; +}FILELOCK_DATA, *PFILELOCK_DATA; + +typedef struct _SHAREDEXCLUSIVEUNLOCK +{ + BOOLEAN State[TEST_NO]; + ULONG NumberfLocks; + NTSTATUS Status; + ULONG LastLock; +}SHAREDEXCLUSIVEUNLOCK, *PSHAREDEXCLUSIVEUNLOCK; + +typedef struct _SHAREDEXCLUSIVEOVERLAPPED +{ + BOOLEAN FirstState; + BOOLEAN SecondStates[TEST_NO]; + ULONG NumberLocks; + NTSTATUS Status; + ULONG LastLocks; +}SHAREDEXCLUSIVEOVERLAPPED, *PSHAREDEXCLUSIVEOVERLAPPED; + +typedef struct _SHAREDEXCLUSIVELOCKS +{ + BOOLEAN State; + ULONG NumberLocks; + BOOLEAN Read [TEST_NO]; + BOOLEAN Write[TEST_NO]; + NTSTATUS Status[TEST_NO]; + ULONG LastNumOfLocks; +}SHAREDEXCLUSIVELOCKS, *PSHAREDEXCLUSIVELOCKS; + + +SHAREDEXCLUSIVELOCKS ExclusiveResults[] = +{ + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS }, 0x00000000 } + +}; + +SHAREDEXCLUSIVELOCKS SharedResults[] = +{ + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, \ + { STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, FALSE, TRUE, TRUE, FALSE, FALSE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, TRUE, TRUE, FALSE, FALSE, FALSE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, FALSE, TRUE, FALSE, FALSE, FALSE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS, STATUS_RANGE_NOT_LOCKED }, 0x00000000 }, + + { TRUE, 0x00000001, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}, { TRUE, FALSE, TRUE, FALSE, FALSE, FALSE }, \ + { STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_RANGE_NOT_LOCKED, STATUS_SUCCESS }, 0x00000000 } +}; + +SHAREDEXCLUSIVEOVERLAPPED ExclusiveSharedResults[] = +{ + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000004, 0x00000000, 0x00000000 } +}; + +SHAREDEXCLUSIVEOVERLAPPED SharedExclusiveResults[] = + { + { TRUE, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, 0x00000005, 0x00000000, 0x00000000 }, + + { TRUE, { FALSE, FALSE, TRUE, TRUE, FALSE, FALSE }, 0x00000003, 0x00000000, 0x00000000 }, + + { TRUE, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, 0x00000005, 0x00000000, 0x00000000 }, + + { TRUE, { FALSE, TRUE, TRUE, FALSE, FALSE, FALSE }, 0x00000003, 0x00000000, 0x00000000 }, + + { TRUE, { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, 0x00000001, 0x00000000, 0x00000000 }, + + { TRUE, { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, 0x00000001, 0x00000000, 0x00000000 } + }; + +SHAREDEXCLUSIVEUNLOCK UnLockSharedByKeyResults = +{ + { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000003, STATUS_SUCCESS, 0x00000000 +}; + +SHAREDEXCLUSIVEUNLOCK UnLockExclusiveByKeyResults = +{ + { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, 0x00000004, STATUS_SUCCESS, 0x00000000 +}; + +SHAREDEXCLUSIVEUNLOCK UnLockSharedNoKeyResults = +{ + { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, 0x00000003, STATUS_SUCCESS, 0x00000000 +}; + +SHAREDEXCLUSIVEUNLOCK UnLockExclusiveNoKeyResults = +{ + { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, 0x00000004, STATUS_SUCCESS, 0x00000000 +}; + + +TEST_DATA LockTestData[] = +{ /* File offset */ /*Length */ + { { 0x00000000 }, { 0x00000000 } }, + { { 0x00000000 }, { 0x00000004 } }, + { { 0x00000004 }, { 0x00000000 } }, + { { 0x00000008 }, { 0x00000016 } }, + { { 0x00000000 }, { 0x00000064 } }, + { { 0x00000000 }, { 0x00000128 } } +}; + +FILELOCK_DATA FileLockData; + +ULONG GetNumberOfLocks(PFILE_LOCK FileLock) +{ + ULONG NumberOfLocks = 0; + for (FileLockData.LockInfo = FsRtlGetNextFileLock(FileLockData.FileLock, TRUE); + FileLockData.LockInfo != NULL; + FileLockData.LockInfo = FsRtlGetNextFileLock(FileLockData.FileLock, FALSE)) { + NumberOfLocks++; + } + return NumberOfLocks; +} + +VOID InitalizeLockInfoData (VOID) +{ + ULONG i; + FileLockData.ProcessId = PsGetCurrentProcess(); + FileLockData.FileLock = FsRtlAllocateFileLock(NULL, NULL); + + for (i = 0; i < TEST_NO; i++) + { + FileLockData.FileOffset[i].QuadPart = LockTestData[i].FileOffset.QuadPart; + FileLockData.Length[i].QuadPart = LockTestData[i].Length.QuadPart; + + FileLockData.TestData[i].FileOffset.QuadPart = LockTestData[i].FileOffset.QuadPart; + FileLockData.TestData[i].Length.QuadPart = LockTestData[i].Length.QuadPart; + } +} + +VOID InitalizeFile(VOID) +{ + RtlInitAnsiString(&StringName, FileName); + + RtlAnsiStringToUnicodeString(&UnicodeFileName, + &StringName, + TRUE); + + InitializeObjectAttributes(&Attributes, + &UnicodeFileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwCreateFile(&GlobalHandle, + GENERIC_WRITE, + &Attributes, + &IoStatus, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + RtlFreeUnicodeString(&UnicodeFileName); + + if (!NT_SUCCESS(Status)) + { + trace("Error creating file - with status %0x%08x", Status); + return; + } + + Status = ObReferenceObjectByHandle(GlobalHandle, + GENERIC_ALL, + IoFileObjectType, + KernelMode, + &FileLockData.FileObject, + NULL); + + if (!NT_SUCCESS(Status)) + { + trace("Error getting FILE_OBJECT - with status 0x%08x", Status); + return; + } + + Status = ZwWriteFile(GlobalHandle, + NULL, + NULL, + NULL, + &IoStatus, + WriteBuffer, + strlen(WriteBuffer), + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + trace("Write failed - with status 0x%08x",Status); + return; + } + return; +} + +VOID TestSharedExclusiveLocks(BOOLEAN SharedExclusive, PSHAREDEXCLUSIVELOCKS Expected ) +{ + ULONG i, j, k, NumberOfLocks; + NTSTATUS Status; + BOOLEAN State; + + for (j = 0; j < TEST_NO; j++) + { + FsRtlInitializeFileLock(FileLockData.FileLock, NULL, NULL); + State = FsRtlPrivateLock(FileLockData.FileLock, + FileLockData.FileObject, + &FileLockData.FileOffset[j], + &FileLockData.Length[j], + FileLockData.ProcessId, + NO_KEY, + FALSE, + SharedExclusive, + &FileLockData.IoStatus, + NULL, + NULL, + FALSE); + ok( State == Expected[j].State,"FsRtlPrivateLock[%d] - Expected %d, got %d \n", j, Expected[j].State, State); + + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok(NumberOfLocks == Expected[j].NumberLocks,"GetNumberOfLocks[%d] - Expected %d, got %d \n",j , Expected[j].NumberLocks, NumberOfLocks); + + for ( i = 0; i < TEST_NO; i++) + { + State = FsRtlFastCheckLockForRead(FileLockData.FileLock, + &FileLockData.TestData[i].FileOffset, + &FileLockData.TestData[i].Length, + NO_KEY, + FileLockData.FileObject, + FileLockData.ProcessId); + ok(State == Expected[j].Read[i],"FsRtlFastCheckLockForRead[%d][%d] - Expected %d, got %d \n", j, i, Expected[j].Read[i], State); + + State = FsRtlFastCheckLockForWrite(FileLockData.FileLock, + &FileLockData.TestData[i].FileOffset, + &FileLockData.TestData[i].Length, + NO_KEY, + FileLockData.FileObject, + FileLockData.ProcessId); + ok( State == Expected[j].Write[i],"FsRtlFastCheckLockForWrite[%d][%d] - Expected %d, got %d \n", j, i, Expected[j].Write[i], State); + } + + for (k = 0; k < TEST_NO; k++) + { + Status = FsRtlFastUnlockSingle(FileLockData.FileLock, + FileLockData.FileObject, + &FileLockData.FileOffset[k], + &FileLockData.Length[k], + FileLockData.ProcessId, + NO_KEY, + NULL, + FALSE); //Always FALSE?? + ok(Status == Expected[j].Status[k],"FsRtlFastUnlockSingle[%d][%d] - Expected %d, got %d \n", j, k, Expected[j].Status[k], Status); + } + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok(NumberOfLocks == Expected[j].LastNumOfLocks,"GetNumberOfLocks[%d] - Expected %d, got %d \n", j, Expected[j].LastNumOfLocks, NumberOfLocks); + + FsRtlUninitializeFileLock(FileLockData.FileLock); + } +} + +VOID TestFsRtlFastUnlockAllOrByKey(ULONG Key, BOOLEAN SharedExclusive, SHAREDEXCLUSIVEUNLOCK Expected) +{ + NTSTATUS Status; + BOOLEAN State; + ULONG i, NumberOfLocks; + + FsRtlInitializeFileLock(FileLockData.FileLock, NULL, NULL); + + for (i = 0; i < TEST_NO; i++) + { + State = FsRtlPrivateLock(FileLockData.FileLock, + FileLockData.FileObject, + &FileLockData.FileOffset[i], + &FileLockData.Length[i], + FileLockData.ProcessId, + Key, + FALSE, + SharedExclusive, + &FileLockData.IoStatus, + NULL, + NULL, + FALSE); + ok(State == Expected.State[i], "FsRtlPrivateLock[%d] - Expected %d, got %d \n", i, Expected.State[i], State); + } + + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok(NumberOfLocks == Expected.NumberfLocks, "GetNumberOfLocks - Expected %d, got %d \n", Expected.NumberfLocks, NumberOfLocks); + + Status = FsRtlFastUnlockAllByKey(FileLockData.FileLock, + FileLockData.FileObject, + FileLockData.ProcessId, + Key, + NULL); + ok(Status == Expected.Status, "FsRtlFastUnlockAllByKey - Expected 0x%08x, got 0x%08x \n", Expected.Status, Status); + + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok(NumberOfLocks == Expected.LastLock,"GetNumberOfLocks - Expected %d, got %d\n", Expected.LastLock, NumberOfLocks); + + FsRtlUninitializeFileLock(FileLockData.FileLock); +} + +VOID TestSharedExclusiveLocksOverlapped(BOOLEAN FirstLockSharedExclusive, BOOLEAN SecondLockSharedExclusive, PSHAREDEXCLUSIVEOVERLAPPED Expected) +{ + NTSTATUS Status; + BOOLEAN State; + ULONG i, j, NumberOfLocks; + + for (i = 0; i < TEST_NO; i++) + { + FsRtlInitializeFileLock(FileLockData.FileLock, NULL, NULL); + State = FsRtlPrivateLock(FileLockData.FileLock, + FileLockData.FileObject, + &FileLockData.FileOffset[i], + &FileLockData.Length[i], + FileLockData.ProcessId, + NO_KEY, + FALSE, + FirstLockSharedExclusive, + &FileLockData.IoStatus, + NULL, + NULL, + FALSE); + ok(State == Expected[i].FirstState, "[FirstLock]FsRtlPrivateLock[%d] - Expected %d, got %d \n",i , Expected[i].FirstState, State); + + for (j = 0; j < TEST_NO; j++) + { + State = FsRtlPrivateLock(FileLockData.FileLock, + FileLockData.FileObject, + &FileLockData.FileOffset[j], + &FileLockData.Length[j], + FileLockData.ProcessId, + NO_KEY, + FALSE, + SecondLockSharedExclusive, + &FileLockData.IoStatus, + NULL, + NULL, + FALSE); + ok(State == Expected[i].SecondStates[j], "[SecondLock]FsRtlPrivateLock[%d][%d] - Expected %d, got %d \n",i, j, Expected[i].SecondStates[j], State); + } + + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok(NumberOfLocks == Expected[i].NumberLocks, "GetNumberOfLocks[%d] - Expected %d, got %d \n",i , Expected[i].NumberLocks, NumberOfLocks); + + Status = FsRtlFastUnlockAll(FileLockData.FileLock, + FileLockData.FileObject, + FileLockData.ProcessId, + NULL); + ok(Status == Expected[i].Status,"FsRtlFastUnlockAll[%d] - Expected 0x%08x, got 0x%08x \n", i, Expected[i].Status, Status); + + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + ok (NumberOfLocks == Expected[i].LastLocks,"GetNumberOfLocks[%d] - Expected %d, got %d \n", i, Expected[i].LastLocks, NumberOfLocks); + + FsRtlUninitializeFileLock(FileLockData.FileLock); + } +} + +START_TEST(FsRtlLockFile) +{ + ULONG NumberOfLocks; + + InitalizeFile(); + InitalizeLockInfoData(); + TestSharedExclusiveLocks(TRUE, ExclusiveResults); + TestSharedExclusiveLocks(FALSE, SharedResults); + + TestSharedExclusiveLocksOverlapped(TRUE, FALSE, ExclusiveSharedResults); + TestSharedExclusiveLocksOverlapped(FALSE, TRUE, SharedExclusiveResults); + + TestFsRtlFastUnlockAllOrByKey(0, TRUE, UnLockExclusiveNoKeyResults); + TestFsRtlFastUnlockAllOrByKey(0x12345678, FALSE,UnLockSharedByKeyResults); + TestFsRtlFastUnlockAllOrByKey(0, FALSE, UnLockSharedNoKeyResults); + TestFsRtlFastUnlockAllOrByKey(0x87654321, TRUE, UnLockExclusiveByKeyResults); + + FsRtlFreeFileLock(FileLockData.FileLock); + NumberOfLocks = GetNumberOfLocks(FileLockData.FileLock); + + ok(NumberOfLocks == 0,"GetNumberOfLocks - Expected %d, got %d ", 0, NumberOfLocks); + ZwClose(GlobalHandle); +}