Author: tfaber Date: Sat Sep 5 11:49:54 2015 New Revision: 69022
URL: http://svn.reactos.org/svn/reactos?rev=69022&view=rev Log: [NTOS:PS] - Implement NtApphelpCacheControl. Patch by Mark Jansen CORE-9914 #resolve
Added: trunk/reactos/ntoskrnl/ps/apphelp.c (with props) trunk/rostests/apitests/ntdll/NtApphelpCacheControl.c (with props) Modified: trunk/reactos/boot/bootdata/hivesys.inf trunk/reactos/ntoskrnl/include/internal/ps.h trunk/reactos/ntoskrnl/io/iomgr/iomgr.c trunk/reactos/ntoskrnl/ntos.cmake trunk/reactos/ntoskrnl/po/poshtdwn.c trunk/reactos/ntoskrnl/ps/psmgr.c trunk/rostests/apitests/ntdll/CMakeLists.txt trunk/rostests/apitests/ntdll/testlist.c
Modified: trunk/reactos/boot/bootdata/hivesys.inf URL: http://svn.reactos.org/svn/reactos/trunk/reactos/boot/bootdata/hivesys.inf?r... ============================================================================== --- trunk/reactos/boot/bootdata/hivesys.inf [iso-8859-1] (original) +++ trunk/reactos/boot/bootdata/hivesys.inf [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -1280,6 +1280,9 @@ HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager","ObjectDirectories",0x00010000, \ "\Windows", \ "\RPC Control" +# This is an empty app compat cache +HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache","AppCompatCache", 0x00000001, \ + fe,0f,dc,ba,00,00,00,00
; DOS devices HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices","AUX",0x00000000,"\DosDevices\COM1"
Modified: trunk/reactos/ntoskrnl/include/internal/ps.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/p... ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/ps.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/ps.h [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -411,6 +411,18 @@ BOOLEAN NTAPI PspIsProcessExiting(IN PEPROCESS Process); + +// +// Apphelp functions +// +NTSTATUS +NTAPI +INIT_FUNCTION +ApphelpCacheInitialize(VOID); + +VOID +NTAPI +ApphelpCacheShutdown(VOID);
// // Global data inside the Process Manager
Modified: trunk/reactos/ntoskrnl/io/iomgr/iomgr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/io/iomgr/iomgr.c?r... ============================================================================== --- trunk/reactos/ntoskrnl/io/iomgr/iomgr.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/io/iomgr/iomgr.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -531,6 +531,9 @@ /* Initialize PnP manager */ IopInitializePlugPlayServices();
+ /* Initialize SHIM engine */ + ApphelpCacheInitialize(); + /* Initialize WMI */ WmiInitialize();
Modified: trunk/reactos/ntoskrnl/ntos.cmake URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ntos.cmake?rev=690... ============================================================================== --- trunk/reactos/ntoskrnl/ntos.cmake [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ntos.cmake [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -248,6 +248,7 @@ ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/job.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/kill.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/process.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/apphelp.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/psmgr.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/psnotify.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/query.c
Modified: trunk/reactos/ntoskrnl/po/poshtdwn.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/po/poshtdwn.c?rev=... ============================================================================== --- trunk/reactos/ntoskrnl/po/poshtdwn.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/po/poshtdwn.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -266,6 +266,9 @@ /* First, the HAL handles any "end of boot" special functionality */ DPRINT("HAL shutting down\n"); HalEndOfBoot(); + + /* Shut down the Shim cache if enabled */ + ApphelpCacheShutdown();
/* In this step, the I/O manager does first-chance shutdown notification */ DPRINT("I/O manager shutting down in phase 0\n");
Added: trunk/reactos/ntoskrnl/ps/apphelp.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ps/apphelp.c?rev=6... ============================================================================== --- trunk/reactos/ntoskrnl/ps/apphelp.c (added) +++ trunk/reactos/ntoskrnl/ps/apphelp.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -0,0 +1,739 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/ps/apphelp.c + * PURPOSE: SHIM engine caching. + * This caching speeds up checks for the apphelp compatibility layer. + * PROGRAMMERS: Mark Jansen + */ + +/* +Useful references: +https://github.com/mandiant/ShimCacheParser/blob/master/ShimCacheParser.py +http://technet.microsoft.com/en-us/library/dd837644(v=ws.10).aspx +http://msdn.microsoft.com/en-us/library/bb432182(v=vs.85).aspx +http://www.alex-ionescu.com/?p=43 +http://recxltd.blogspot.nl/2012/04/windows-appcompat-research-notes-part-1.h... +http://journeyintoir.blogspot.ch/2013/12/revealing-recentfilecachebcf-file.h... +https://dl.mandiant.com/EE/library/Whitepaper_ShimCacheParser.pdf +*/ + +/* INCLUDES ******************************************************************/ + +#include <ntoskrnl.h> +#define NDEBUG +#include <debug.h> + +/* GLOBALS *******************************************************************/ + +static BOOLEAN ApphelpCacheEnabled = FALSE; +static ERESOURCE ApphelpCacheLock; +static RTL_AVL_TABLE ApphelpShimCache; +static LIST_ENTRY ApphelpShimCacheAge; + +extern ULONG InitSafeBootMode; + +static UNICODE_STRING AppCompatCacheKey = RTL_CONSTANT_STRING(L"\Registry\MACHINE\System\CurrentControlSet\Control\Session Manager\AppCompatCache"); +static OBJECT_ATTRIBUTES AppCompatKeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&AppCompatCacheKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); +static UNICODE_STRING AppCompatCacheValue = RTL_CONSTANT_STRING(L"AppCompatCache"); + +#define EMPTY_SHIM_ENTRY { { 0 }, { { 0 } }, 0 } +#define MAX_SHIM_ENTRIES 0x200 +#define TAG_SHIM 'MIHS' + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (HANDLE)(-1) +#endif + +#include <pshpack1.h> + +typedef struct SHIM_PERSISTENT_CACHE_HEADER_52 +{ + ULONG Magic; + ULONG NumEntries; +} SHIM_PERSISTENT_CACHE_HEADER_52, *PSHIM_PERSISTENT_CACHE_HEADER_52; + +/* The data that is present in the registry (Win2k3 version) */ +typedef struct SHIM_PERSISTENT_CACHE_ENTRY_52 +{ + UNICODE_STRING ImageName; + LARGE_INTEGER DateTime; + LARGE_INTEGER FileSize; +} SHIM_PERSISTENT_CACHE_ENTRY_52, *PSHIM_PERSISTENT_CACHE_ENTRY_52; + +#include <poppack.h> + +#define CACHE_MAGIC_NT_52 0xbadc0ffe +#define CACHE_HEADER_SIZE_NT_52 0x8 +#define NT52_PERSISTENT_ENTRY_SIZE32 0x18 +#define NT52_PERSISTENT_ENTRY_SIZE64 0x20 + +//#define CACHE_MAGIC_NT_61 0xbadc0fee +//#define CACHE_HEADER_SIZE_NT_61 0x80 +//#define NT61_PERSISTENT_ENTRY_SIZE32 0x20 +//#define NT61_PERSISTENT_ENTRY_SIZE64 0x30 + +#define SHIM_CACHE_MAGIC CACHE_MAGIC_NT_52 +#define SHIM_CACHE_HEADER_SIZE CACHE_HEADER_SIZE_NT_52 +#ifdef _WIN64 +#define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE64 +#else +#define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE32 +#endif +#define SHIM_PERSISTENT_CACHE_HEADER SHIM_PERSISTENT_CACHE_HEADER_52 +#define PSHIM_PERSISTENT_CACHE_HEADER PSHIM_PERSISTENT_CACHE_HEADER_52 +#define SHIM_PERSISTENT_CACHE_ENTRY SHIM_PERSISTENT_CACHE_ENTRY_52 +#define PSHIM_PERSISTENT_CACHE_ENTRY PSHIM_PERSISTENT_CACHE_ENTRY_52 + +C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_ENTRY) == SHIM_PERSISTENT_CACHE_ENTRY_SIZE); +C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_HEADER) == SHIM_CACHE_HEADER_SIZE); + +/* The struct we keep in memory */ +typedef struct SHIM_CACHE_ENTRY +{ + LIST_ENTRY List; + SHIM_PERSISTENT_CACHE_ENTRY Persistent; + ULONG CompatFlags; +} SHIM_CACHE_ENTRY, *PSHIM_CACHE_ENTRY; + +/* PRIVATE FUNCTIONS *********************************************************/ + +PVOID +ApphelpAlloc( + _In_ ULONG ByteSize) +{ + return ExAllocatePoolWithTag(PagedPool, ByteSize, TAG_SHIM); +} + +VOID +ApphelpFree( + _In_ PVOID Data) +{ + ExFreePoolWithTag(Data, TAG_SHIM); +} + +VOID +ApphelpCacheAcquireLock(VOID) +{ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&ApphelpCacheLock, TRUE); +} + +BOOLEAN +ApphelpCacheTryAcquireLock(VOID) +{ + KeEnterCriticalRegion(); + if (!ExTryToAcquireResourceExclusiveLite(&ApphelpCacheLock)) + { + KeLeaveCriticalRegion(); + return FALSE; + } + return TRUE; +} + +VOID +ApphelpCacheReleaseLock(VOID) +{ + ExReleaseResourceLite(&ApphelpCacheLock); + KeLeaveCriticalRegion(); +} + +VOID +ApphelpDuplicateUnicodeString( + _Out_ PUNICODE_STRING Destination, + _In_ PCUNICODE_STRING Source) +{ + Destination->Length = Source->Length; + if (Destination->Length) + { + Destination->MaximumLength = Destination->Length + sizeof(WCHAR); + Destination->Buffer = ApphelpAlloc(Destination->MaximumLength); + RtlCopyMemory(Destination->Buffer, Source->Buffer, Destination->Length); + Destination->Buffer[Destination->Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + Destination->MaximumLength = 0; + Destination->Buffer = NULL; + } +} + +VOID +ApphelpFreeUnicodeString( + _Inout_ PUNICODE_STRING String) +{ + if (String->Buffer) + { + ApphelpFree(String->Buffer); + } + String->Length = 0; + String->MaximumLength = 0; + String->Buffer = NULL; +} + +/* Query file info from a handle, storing it in Entry */ +NTSTATUS +ApphelpCacheQueryInfo( + _In_ HANDLE ImageHandle, + _Out_ PSHIM_CACHE_ENTRY Entry) +{ + IO_STATUS_BLOCK IoStatusBlock; + FILE_BASIC_INFORMATION FileBasic; + FILE_STANDARD_INFORMATION FileStandard; + NTSTATUS Status; + + Status = ZwQueryInformationFile(ImageHandle, &IoStatusBlock, + &FileBasic, sizeof(FileBasic), FileBasicInformation); + if (NT_SUCCESS(Status)) + { + Status = ZwQueryInformationFile(ImageHandle, &IoStatusBlock, + &FileStandard, sizeof(FileStandard), FileStandardInformation); + if (NT_SUCCESS(Status)) + { + Entry->Persistent.DateTime = FileBasic.LastWriteTime; + Entry->Persistent.FileSize = FileStandard.EndOfFile; + } + } + return Status; +} + +RTL_GENERIC_COMPARE_RESULTS +NTAPI +ApphelpShimCacheCompareRoutine( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID FirstStruct, + _In_ PVOID SecondStruct) +{ + LONG lResult = RtlCompareUnicodeString( + &((PSHIM_CACHE_ENTRY)FirstStruct)->Persistent.ImageName, + &((PSHIM_CACHE_ENTRY)SecondStruct)->Persistent.ImageName, TRUE); + + if (lResult < 0) + return GenericLessThan; + else if (lResult == 0) + return GenericEqual; + return GenericGreaterThan; +} + +PVOID +NTAPI +ApphelpShimCacheAllocateRoutine( + _In_ PRTL_AVL_TABLE Table, + _In_ CLONG ByteSize) +{ + return ApphelpAlloc(ByteSize); +} + +VOID +NTAPI +ApphelpShimCacheFreeRoutine( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer) +{ + ApphelpFree(Buffer); +} + +NTSTATUS +ApphelpCacheParse( + _In_reads_(DataLength) PUCHAR Data, + _In_ ULONG DataLength) +{ + PSHIM_PERSISTENT_CACHE_HEADER Header = (PSHIM_PERSISTENT_CACHE_HEADER)Data; + + if (DataLength < CACHE_HEADER_SIZE_NT_52) + { + DPRINT1("SHIMS: ApphelpCacheParse not enough data for a minimal header (0x%x)\n", DataLength); + return STATUS_INVALID_PARAMETER; + } + if (Header->Magic == SHIM_CACHE_MAGIC) + { + ULONG Cur; + ULONG NumEntries = Header->NumEntries; + DPRINT1("SHIMS: ApphelpCacheParse walking %d entries\n", NumEntries); + for (Cur = 0; Cur < NumEntries; ++Cur) + { + UNICODE_STRING String; + SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY; + PSHIM_CACHE_ENTRY Result; + PSHIM_PERSISTENT_CACHE_ENTRY pPersistent = + (PSHIM_PERSISTENT_CACHE_ENTRY)(Data + SHIM_CACHE_HEADER_SIZE + + (Cur * SHIM_PERSISTENT_CACHE_ENTRY_SIZE)); + /* The entry in the Persitent storage is not really a UNICODE_STRING, + so we have to convert the offset into a real pointer before using it. */ + String.Length = pPersistent->ImageName.Length; + String.MaximumLength = pPersistent->ImageName.MaximumLength; + String.Buffer = (PWCHAR)((ULONG_PTR)pPersistent->ImageName.Buffer + Data); + + /* Now we copy all data to a local buffer, that can be safely duplicated by RtlInsert */ + Entry.Persistent = *pPersistent; + ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, &String); + Result = RtlInsertElementGenericTableAvl(&ApphelpShimCache, &Entry, sizeof(Entry), NULL); + if (!Result) + { + DPRINT1("SHIMS: ApphelpCacheParse insert failed\n"); + ApphelpFreeUnicodeString(&Entry.Persistent.ImageName); + return STATUS_INVALID_PARAMETER; + } + InsertTailList(&ApphelpShimCacheAge, &Result->List); + } + return STATUS_SUCCESS; + } + DPRINT1("SHIMS: ApphelpCacheParse found invalid magic (0x%x)\n", Header->Magic); + return STATUS_INVALID_PARAMETER; +} + +BOOLEAN +ApphelpCacheRead(VOID) +{ + HANDLE KeyHandle; + NTSTATUS Status; + KEY_VALUE_PARTIAL_INFORMATION KeyValueObject; + PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = &KeyValueObject; + ULONG KeyInfoSize, ResultSize; + + Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &AppCompatKeyAttributes); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("SHIMS: ApphelpCacheRead could not even open Session Manager\AppCompatCache (0x%x)\n", Status); + return FALSE; + } + + Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue, + KeyValuePartialInformation, KeyValueInformation, + sizeof(KeyValueObject), &ResultSize); + + if (Status == STATUS_BUFFER_OVERFLOW) + { + KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyValueInformation->DataLength; + KeyValueInformation = ApphelpAlloc(KeyInfoSize); + if (KeyValueInformation != NULL) + { + Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue, + KeyValuePartialInformation, KeyValueInformation, + KeyInfoSize, &ResultSize); + } + } + + if (NT_SUCCESS(Status) && KeyValueInformation->Type == REG_BINARY) + { + Status = ApphelpCacheParse(KeyValueInformation->Data, + KeyValueInformation->DataLength); + } + else + { + DPRINT1("SHIMS: ApphelpCacheRead not loaded from registry (0x%x)\n", Status); + } + + if (KeyValueInformation != &KeyValueObject && KeyValueInformation != NULL) + ApphelpFree(KeyValueInformation); + + ZwClose(KeyHandle); + return NT_SUCCESS(Status); +} + +BOOLEAN +ApphelpCacheWrite(VOID) +{ + ULONG Length = SHIM_CACHE_HEADER_SIZE; + ULONG NumEntries = 0; + PLIST_ENTRY ListEntry; + PUCHAR Buffer, BufferNamePos; + PSHIM_PERSISTENT_CACHE_HEADER Header; + PSHIM_PERSISTENT_CACHE_ENTRY WriteEntry; + HANDLE KeyHandle; + NTSTATUS Status; + + /* First we have to calculate the required size. */ + ApphelpCacheAcquireLock(); + ListEntry = ApphelpShimCacheAge.Flink; + while (ListEntry != &ApphelpShimCacheAge) + { + PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); + Length += SHIM_PERSISTENT_CACHE_ENTRY_SIZE; + Length += Entry->Persistent.ImageName.MaximumLength; + ++NumEntries; + ListEntry = ListEntry->Flink; + } + DPRINT1("SHIMS: ApphelpCacheWrite, %d Entries, total size: %d\n", NumEntries, Length); + Length = ROUND_UP(Length, sizeof(ULONGLONG)); + DPRINT1("SHIMS: ApphelpCacheWrite, Rounded to: %d\n", Length); + + /* Now we allocate and prepare some helpers */ + Buffer = ApphelpAlloc(Length); + BufferNamePos = Buffer + Length; + Header = (PSHIM_PERSISTENT_CACHE_HEADER)Buffer; + WriteEntry = (PSHIM_PERSISTENT_CACHE_ENTRY)(Buffer + SHIM_CACHE_HEADER_SIZE); + + Header->Magic = SHIM_CACHE_MAGIC; + Header->NumEntries = NumEntries; + + ListEntry = ApphelpShimCacheAge.Flink; + while (ListEntry != &ApphelpShimCacheAge) + { + PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); + USHORT ImageNameLen = Entry->Persistent.ImageName.MaximumLength; + /* Copy the Persistent structure over */ + *WriteEntry = Entry->Persistent; + BufferNamePos -= ImageNameLen; + /* Copy the image name over */ + RtlCopyMemory(BufferNamePos, Entry->Persistent.ImageName.Buffer, ImageNameLen); + /* Fix the Persistent structure, so that Buffer is once again an offset */ + WriteEntry->ImageName.Buffer = (PWCH)(BufferNamePos - Buffer); + + ++WriteEntry; + ListEntry = ListEntry->Flink; + } + ApphelpCacheReleaseLock(); + + Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &AppCompatKeyAttributes); + if (NT_SUCCESS(Status)) + { + Status = ZwSetValueKey(KeyHandle, &AppCompatCacheValue, 0, REG_BINARY, Buffer, Length); + ZwClose(KeyHandle); + } + else + { + DPRINT1("SHIMS: ApphelpCacheWrite could not even open Session Manager\AppCompatCache (0x%x)\n", Status); + } + + ApphelpFree(Buffer); + return NT_SUCCESS(Status); +} + + +NTSTATUS +NTAPI +INIT_FUNCTION +ApphelpCacheInitialize(VOID) +{ + DPRINT1("SHIMS: ApphelpCacheInitialize\n"); + /* If we are booting in safemode we do not want to use the apphelp cache */ + if (InitSafeBootMode) + { + DPRINT1("SHIMS: Safe mode detected, disabling cache.\n"); + ApphelpCacheEnabled = FALSE; + } + else + { + ExInitializeResourceLite(&ApphelpCacheLock); + RtlInitializeGenericTableAvl(&ApphelpShimCache, + ApphelpShimCacheCompareRoutine, + ApphelpShimCacheAllocateRoutine, + ApphelpShimCacheFreeRoutine, + NULL); + InitializeListHead(&ApphelpShimCacheAge); + ApphelpCacheEnabled = ApphelpCacheRead(); + } + DPRINT1("SHIMS: ApphelpCacheInitialize: %d\n", ApphelpCacheEnabled); + return STATUS_SUCCESS; +} + +VOID +NTAPI +ApphelpCacheShutdown(VOID) +{ + if (ApphelpCacheEnabled) + { + ApphelpCacheWrite(); + } +} + +NTSTATUS +ApphelpValidateData( + _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData, + _Out_ PUNICODE_STRING ImageName, + _Out_ PHANDLE ImageHandle) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if (ServiceData) + { + UNICODE_STRING LocalImageName; + _SEH2_TRY + { + ProbeForRead(ServiceData, sizeof(APPHELP_CACHE_SERVICE_LOOKUP), sizeof(ULONG)); + LocalImageName = ServiceData->ImageName; + *ImageHandle = ServiceData->ImageHandle; + if (LocalImageName.Length && LocalImageName.Buffer) + { + ProbeForRead(LocalImageName.Buffer, LocalImageName.Length * sizeof(WCHAR), 1); + ApphelpDuplicateUnicodeString(ImageName, &LocalImageName); + Status = STATUS_SUCCESS; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + if (!NT_SUCCESS(Status)) + { + DPRINT1("SHIMS: ApphelpValidateData: invalid data passed\n"); + } + return Status; +} + +NTSTATUS +ApphelpCacheRemoveEntryNolock( + _In_ PSHIM_CACHE_ENTRY Entry) +{ + if (Entry) + { + PWSTR Buffer = Entry->Persistent.ImageName.Buffer; + RemoveEntryList(&Entry->List); + if (RtlDeleteElementGenericTableAvl(&ApphelpShimCache, Entry)) + ApphelpFree(Buffer); + return STATUS_SUCCESS; + } + return STATUS_NOT_FOUND; +} + +NTSTATUS +ApphelpCacheLookupEntry( + _In_ PUNICODE_STRING ImageName, + _In_ HANDLE ImageHandle) +{ + NTSTATUS Status = STATUS_NOT_FOUND; + + if (ApphelpCacheTryAcquireLock()) + { + SHIM_CACHE_ENTRY Lookup = EMPTY_SHIM_ENTRY; + PSHIM_CACHE_ENTRY Entry; + Lookup.Persistent.ImageName = *ImageName; + Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, &Lookup); + if (Entry) + { + DPRINT1("SHIMS: ApphelpCacheLookupEntry: found %wZ\n", ImageName); + if (ImageHandle == INVALID_HANDLE_VALUE) + { + DPRINT1("SHIMS: ApphelpCacheLookupEntry: ok\n"); + /* just return if we know it, do not query file info */ + Status = STATUS_SUCCESS; + } + else if (NT_SUCCESS(ApphelpCacheQueryInfo(ImageHandle, &Lookup)) && + Lookup.Persistent.DateTime.QuadPart == Entry->Persistent.DateTime.QuadPart && + Lookup.Persistent.FileSize.QuadPart == Entry->Persistent.FileSize.QuadPart) + { + DPRINT1("SHIMS: ApphelpCacheLookupEntry: found & validated\n"); + Status = STATUS_SUCCESS; + /* move it to the front to keep it alive */ + RemoveEntryList(&Entry->List); + InsertHeadList(&ApphelpShimCacheAge, &Entry->List); + } + else + { + DPRINT1("SHIMS: ApphelpCacheLookupEntry: file info mismatch\n"); + /* Could not read file info, or it did not match, drop it from the cache */ + ApphelpCacheRemoveEntryNolock(Entry); + } + } + else + { + DPRINT1("SHIMS: ApphelpCacheLookupEntry: could not find %wZ\n", ImageName); + } + ApphelpCacheReleaseLock(); + } + return Status; +} + +NTSTATUS +ApphelpCacheRemoveEntry( + _In_ PUNICODE_STRING ImageName) +{ + PSHIM_CACHE_ENTRY Entry; + NTSTATUS Status; + + ApphelpCacheAcquireLock(); + Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, ImageName); + Status = ApphelpCacheRemoveEntryNolock(Entry); + ApphelpCacheReleaseLock(); + return Status; +} + +/* Validate that we are either called from r0, or from a service-like context */ +NTSTATUS +ApphelpCacheAccessCheck(VOID) +{ + if (ExGetPreviousMode() != KernelMode) + { + if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) + { + DPRINT1("SHIMS: ApphelpCacheAccessCheck failed\n"); + return STATUS_ACCESS_DENIED; + } + } + return STATUS_SUCCESS; +} + +NTSTATUS +ApphelpCacheUpdateEntry( + _In_ PUNICODE_STRING ImageName, + _In_ HANDLE ImageHandle) +{ + NTSTATUS Status = STATUS_SUCCESS; + SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY; + PSHIM_CACHE_ENTRY Lookup; + PVOID NodeOrParent; + TABLE_SEARCH_RESULT SearchResult; + + ApphelpCacheAcquireLock(); + + /* If we got a file handle, query it for info */ + if (ImageHandle != INVALID_HANDLE_VALUE) + { + Status = ApphelpCacheQueryInfo(ImageHandle, &Entry); + } + + if (NT_SUCCESS(Status)) + { + /* Use ImageName for the lookup, don't actually duplicate it */ + Entry.Persistent.ImageName = *ImageName; + Lookup = RtlLookupElementGenericTableFullAvl(&ApphelpShimCache, &Entry, + &NodeOrParent, &SearchResult); + if (Lookup) + { + DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Entry already exists, reusing it\n"); + /* Unlink the found item, so we can put it back at the front, + and copy the earlier obtained file info*/ + RemoveEntryList(&Lookup->List); + Lookup->Persistent.DateTime = Entry.Persistent.DateTime; + Lookup->Persistent.FileSize = Entry.Persistent.FileSize; + } + else + { + DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Inserting new Entry\n"); + /* Insert a new entry, with its own copy of the ImageName */ + ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, ImageName); + Lookup = RtlInsertElementGenericTableFullAvl(&ApphelpShimCache, + &Entry, sizeof(Entry), 0, NodeOrParent, SearchResult); + if (!Lookup) + { + ApphelpFreeUnicodeString(&Entry.Persistent.ImageName); + Status = STATUS_NO_MEMORY; + } + } + if (Lookup) + { + /* Either we re-used an existing item, or we inserted a new one, keep it alive */ + InsertHeadList(&ApphelpShimCacheAge, &Lookup->List); + if (RtlNumberGenericTableElementsAvl(&ApphelpShimCache) > MAX_SHIM_ENTRIES) + { + PSHIM_CACHE_ENTRY Remove; + DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Cache growing too big, dropping oldest item\n"); + Remove = CONTAINING_RECORD(ApphelpShimCacheAge.Blink, SHIM_CACHE_ENTRY, List); + Status = ApphelpCacheRemoveEntryNolock(Remove); + } + } + } + ApphelpCacheReleaseLock(); + return Status; +} + +NTSTATUS +ApphelpCacheFlush(VOID) +{ + PVOID p; + + DPRINT1("SHIMS: ApphelpCacheFlush\n"); + ApphelpCacheAcquireLock(); + while ((p = RtlEnumerateGenericTableAvl(&ApphelpShimCache, TRUE))) + { + ApphelpCacheRemoveEntryNolock((PSHIM_CACHE_ENTRY)p); + } + ApphelpCacheReleaseLock(); + return STATUS_SUCCESS; +} + +NTSTATUS +ApphelpCacheDump(VOID) +{ + PLIST_ENTRY ListEntry; + + DPRINT1("SHIMS: NtApphelpCacheControl( Dumping entries, newset to oldest )\n"); + ApphelpCacheAcquireLock(); + ListEntry = ApphelpShimCacheAge.Flink; + while (ListEntry != &ApphelpShimCacheAge) + { + PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); + DPRINT1("Entry: %S\n", Entry->Persistent.ImageName.Buffer); + DPRINT1("DateTime High: 0x%x, Low: 0x%x\n", + Entry->Persistent.DateTime.HighPart, Entry->Persistent.DateTime.LowPart); + DPRINT1("FileSize High: 0x%x, Low: 0x%x\n", + Entry->Persistent.FileSize.HighPart, Entry->Persistent.FileSize.LowPart); + DPRINT1("Flags: 0x%x\n", Entry->CompatFlags); + ListEntry = ListEntry->Flink; + } + ApphelpCacheReleaseLock(); + return STATUS_SUCCESS; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +NTSTATUS +NTAPI +NtApphelpCacheControl( + _In_ APPHELPCACHESERVICECLASS Service, + _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + UNICODE_STRING ImageName = { 0 }; + HANDLE Handle = INVALID_HANDLE_VALUE; + + if (!ApphelpCacheEnabled) + { + DPRINT1("NtApphelpCacheControl: ApphelpCacheEnabled == 0\n"); + return Status; + } + switch (Service) + { + case ApphelpCacheServiceLookup: + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceLookup )\n"); + Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); + if (NT_SUCCESS(Status)) + Status = ApphelpCacheLookupEntry(&ImageName, Handle); + break; + case ApphelpCacheServiceRemove: + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceRemove )\n"); + Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); + if (NT_SUCCESS(Status)) + Status = ApphelpCacheRemoveEntry(&ImageName); + break; + case ApphelpCacheServiceUpdate: + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceUpdate )\n"); + Status = ApphelpCacheAccessCheck(); + if (NT_SUCCESS(Status)) + { + Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); + if (NT_SUCCESS(Status)) + Status = ApphelpCacheUpdateEntry(&ImageName, Handle); + } + break; + case ApphelpCacheServiceFlush: + Status = ApphelpCacheFlush(); + break; + case ApphelpCacheServiceDump: + Status = ApphelpCacheDump(); + break; + case ApphelpDBGReadRegistry: + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): flushing cache.\n"); + ApphelpCacheFlush(); + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): reading cache.\n"); + Status = ApphelpCacheRead() ? STATUS_SUCCESS : STATUS_NOT_FOUND; + break; + case ApphelpDBGWriteRegistry: + DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGWriteRegistry ): writing cache.\n"); + Status = ApphelpCacheWrite() ? STATUS_SUCCESS : STATUS_NOT_FOUND; + break; + default: + DPRINT1("SHIMS: NtApphelpCacheControl( Invalid service requested )\n"); + break; + } + if (ImageName.Buffer) + { + ApphelpFreeUnicodeString(&ImageName); + } + return Status; +} +
Propchange: trunk/reactos/ntoskrnl/ps/apphelp.c ------------------------------------------------------------------------------ svn:eol-style = native
Modified: trunk/reactos/ntoskrnl/ps/psmgr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ps/psmgr.c?rev=690... ============================================================================== --- trunk/reactos/ntoskrnl/ps/psmgr.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ps/psmgr.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -250,7 +250,7 @@ LARGE_INTEGER Offset = {{0, 0}}; SIZE_T ViewSize = 0; PVOID ImageBase = 0; - + /* Map the System DLL */ Status = MmMapViewOfSection(PspSystemDllSection, Process, @@ -267,7 +267,7 @@ /* Normalize status code */ Status = STATUS_CONFLICTING_ADDRESSES; } - + /* Write the image base and return status */ if (DllBase) *DllBase = ImageBase; return Status; @@ -677,13 +677,4 @@ return (NtBuildNumber >> 28) == 0xC; }
-NTSTATUS -NTAPI -NtApphelpCacheControl(IN APPHELPCACHESERVICECLASS Service, - IN PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - /* EOF */
Modified: trunk/rostests/apitests/ntdll/CMakeLists.txt URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/ntdll/CMakeLists.... ============================================================================== --- trunk/rostests/apitests/ntdll/CMakeLists.txt [iso-8859-1] (original) +++ trunk/rostests/apitests/ntdll/CMakeLists.txt [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -2,6 +2,7 @@ list(APPEND SOURCE LdrEnumResources.c NtAllocateVirtualMemory.c + NtApphelpCacheControl.c NtContinue.c NtCreateFile.c NtCreateThread.c
Added: trunk/rostests/apitests/ntdll/NtApphelpCacheControl.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/ntdll/NtApphelpCa... ============================================================================== --- trunk/rostests/apitests/ntdll/NtApphelpCacheControl.c (added) +++ trunk/rostests/apitests/ntdll/NtApphelpCacheControl.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -0,0 +1,367 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: LGPL - See COPYING.LIB in the top level directory + * PURPOSE: Tests for SHIM engine caching. + * PROGRAMMER: Mark Jansen + */ + +#include <apitest.h> + +#include <windows.h> + +#define WIN32_NO_STATUS +#include <ntndk.h> + +enum ServiceCommands +{ + RegisterShimCacheWithHandle = 128, + RegisterShimCacheWithoutHandle = 129, +}; + + +NTSTATUS CallCacheControl(UNICODE_STRING* PathName, BOOLEAN WithMapping, APPHELPCACHESERVICECLASS Service) +{ + APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; + NTSTATUS Status; + CacheEntry.ImageName = *PathName; + if (WithMapping) + { + OBJECT_ATTRIBUTES LocalObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + InitializeObjectAttributes(&LocalObjectAttributes, PathName, + OBJ_CASE_INSENSITIVE, NULL, NULL); + Status = NtOpenFile(&CacheEntry.ImageHandle, + SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE, + &LocalObjectAttributes, &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); + ok_ntstatus(Status, STATUS_SUCCESS); + } + else + { + CacheEntry.ImageHandle = INVALID_HANDLE_VALUE; + } + Status = NtApphelpCacheControl(Service, &CacheEntry); + if (CacheEntry.ImageHandle != INVALID_HANDLE_VALUE) + NtClose(CacheEntry.ImageHandle); + return Status; +} + +int InitEnv(UNICODE_STRING* PathName) +{ + NTSTATUS Status = CallCacheControl(PathName, FALSE, ApphelpCacheServiceRemove); + if (Status == STATUS_INVALID_PARAMETER) + { + /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */ + return 0; + } + ok(Status == STATUS_SUCCESS || Status == STATUS_NOT_FOUND, + "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n", + Status); + return 1; +} + +void CheckValidation(UNICODE_STRING* PathName) +{ + APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; + NTSTATUS Status; + + /* Validate the handling of a NULL pointer */ + Status = NtApphelpCacheControl(ApphelpCacheServiceRemove, NULL); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, NULL); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); + + /* Validate the handling of a NULL pointer inside the struct */ + Status = NtApphelpCacheControl(ApphelpCacheServiceRemove, &CacheEntry); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); + + /* Just call the dump function */ + Status = NtApphelpCacheControl(ApphelpCacheServiceDump, NULL); + ok_ntstatus(Status, STATUS_SUCCESS); + + /* Validate the handling of an invalid handle inside the struct */ + CacheEntry.ImageName = *PathName; + CacheEntry.ImageHandle = (HANDLE)2; + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + /* Validate the handling of an invalid service number */ + Status = NtApphelpCacheControl(999, NULL); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); + Status = NtApphelpCacheControl(999, &CacheEntry); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER); +} + +static BOOLEAN RequestAddition(SC_HANDLE service_handle, BOOLEAN WithMapping) +{ + SERVICE_STATUS Status; + ControlService(service_handle, WithMapping ? RegisterShimCacheWithHandle : + RegisterShimCacheWithoutHandle, &Status); + /* TODO: how to get a return code from the service? */ + return TRUE; +} + +static void RunApphelpCacheControlTests(SC_HANDLE service_handle) +{ + WCHAR szPath[MAX_PATH]; + UNICODE_STRING ntPath; + BOOLEAN Result; + NTSTATUS Status; + APPHELP_CACHE_SERVICE_LOOKUP CacheEntry; + + GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); + Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); + ok(Result == TRUE, "RtlDosPathNameToNtPathName_U\n"); + if (!InitEnv(&ntPath)) + { + skip("NtApphelpCacheControl expects a different structure layout\n"); + return; + } + /* At this point we have made sure that our binary is not present in the cache, + and that the NtApphelpCacheControl function expects the struct layout we use. */ + CheckValidation(&ntPath); + + /* We expect not to find it */ + Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_NOT_FOUND); + Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + /* First we add our process without a file handle (so it will be registered without file info) */ + RequestAddition(service_handle, FALSE); + + /* now we try to find it without validating file info */ + Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_SUCCESS); + /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */ + Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_NOT_FOUND); + /* making the second check without info also fail. */ + Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + + /* Now we add the file with file info */ + RequestAddition(service_handle, TRUE); + + /* so both checks should succeed */ + Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_SUCCESS); + Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_SUCCESS); + + /* We know the file is in the cache now (assuming previous tests succeeded, + let's test invalid handle behavior */ + CacheEntry.ImageName = ntPath; + CacheEntry.ImageHandle = 0; + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + /* re-add it for the next test */ + RequestAddition(service_handle, TRUE); + Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_SUCCESS); + CacheEntry.ImageHandle = (HANDLE)1; + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + /* and again */ + RequestAddition(service_handle, TRUE); + Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); + ok_ntstatus(Status, STATUS_SUCCESS); + CacheEntry.ImageHandle = (HANDLE)0x80000000; + Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); + ok_ntstatus(Status, STATUS_NOT_FOUND); + + RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); +} + + +/* Most service related code was taken from services_winetest:service and modified for usage here + The rest came from MSDN */ + +static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID); +static char service_name[100] = "apphelp_test_service"; +static HANDLE service_stop_event; +static SERVICE_STATUS_HANDLE service_status; + +static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping) +{ + WCHAR szPath[MAX_PATH]; + UNICODE_STRING ntPath; + BOOLEAN Result; + NTSTATUS Status; + GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); + Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); + if (!Result) + { + DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n"); + return FALSE; + } + + Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate); + if (!NT_SUCCESS(Status)) + { + DbgPrint("RegisterInShimCache: CallCacheControl failed\n"); + RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); + return FALSE; + } + RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); + return TRUE; +} + + +static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context) +{ + SERVICE_STATUS status = {0}; + status.dwServiceType = SERVICE_WIN32; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + switch(ctrl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + status.dwCurrentState = SERVICE_STOP_PENDING; + status.dwControlsAccepted = 0; + SetServiceStatus(service_status, &status); + SetEvent(service_stop_event); + return NO_ERROR; + case RegisterShimCacheWithHandle: + if (!RegisterInShimCache(TRUE)) + { + /* TODO: how should we communicate a failure? */ + } + break; + case RegisterShimCacheWithoutHandle: + if (!RegisterInShimCache(FALSE)) + { + /* TODO: how should we communicate a failure? */ + } + break; + default: + DbgPrint("Unhandled: %d\n", ctrl); + break; + } + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(service_status, &status); + return NO_ERROR; +} + +static void WINAPI service_main(DWORD argc, char **argv) +{ + SERVICE_STATUS status = {0}; + service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL); + if(!service_status) + return; + + status.dwServiceType = SERVICE_WIN32; + status.dwCurrentState = SERVICE_RUNNING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + SetServiceStatus(service_status, &status); + + WaitForSingleObject(service_stop_event, INFINITE); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = 0; + SetServiceStatus(service_status, &status); +} + +static SC_HANDLE InstallService(SC_HANDLE scm_handle) +{ + char service_cmd[MAX_PATH+150], *ptr; + SC_HANDLE service; + + ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH); + strcpy(ptr, " NtApphelpCacheControl service"); + ptr += strlen(ptr); + + service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL, + SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, + service_cmd, NULL, NULL, NULL, NULL, NULL); + if (!service) + { + skip("Could not create helper service\n"); + return NULL; + } + return service; +} + +static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp) +{ + DWORD dwBytesNeeded; + DWORD dwStartTime = GetTickCount(); + while (ssp->dwCurrentState != Status) + { + Sleep(40); + if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, + (LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded )) + { + ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status); + break; + } + if ((GetTickCount() - dwStartTime) > 1000) + { + ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n", + Status, ssp->dwCurrentState); + break; + } + } +} + +static void RunTest() +{ + SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); + SC_HANDLE service_handle = InstallService(scm_handle); + if (service_handle) + { + SERVICE_STATUS_PROCESS ssp = {0}; + BOOL res = StartServiceA(service_handle, 0, NULL); + if (res) + { + WaitService(service_handle, SERVICE_RUNNING, &ssp); + RunApphelpCacheControlTests(service_handle); + ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp); + WaitService(service_handle, SERVICE_STOPPED, &ssp); + } + else + { + skip("Could not start helper service\n"); + } + DeleteService(service_handle); + } + CloseServiceHandle(scm_handle); +} + +START_TEST(NtApphelpCacheControl) +{ + char **argv; + int argc; + + pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA"); + if (!pRegisterServiceCtrlHandlerExA) + { + win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n"); + return; + } + argc = winetest_get_mainargs(&argv); + if(argc < 3) + { + RunTest(); + } + else + { + SERVICE_TABLE_ENTRYA servtbl[] = { + {service_name, service_main}, + {NULL, NULL} + }; + service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL); + StartServiceCtrlDispatcherA(servtbl); + Sleep(50); + CloseHandle(service_stop_event); + } +} + +
Propchange: trunk/rostests/apitests/ntdll/NtApphelpCacheControl.c ------------------------------------------------------------------------------ svn:eol-style = native
Modified: trunk/rostests/apitests/ntdll/testlist.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/ntdll/testlist.c?... ============================================================================== --- trunk/rostests/apitests/ntdll/testlist.c [iso-8859-1] (original) +++ trunk/rostests/apitests/ntdll/testlist.c [iso-8859-1] Sat Sep 5 11:49:54 2015 @@ -5,6 +5,7 @@
extern void func_LdrEnumResources(void); extern void func_NtAllocateVirtualMemory(void); +extern void func_NtApphelpCacheControl(void); extern void func_NtContinue(void); extern void func_NtCreateFile(void); extern void func_NtCreateThread(void); @@ -40,6 +41,7 @@ { { "LdrEnumResources", func_LdrEnumResources }, { "NtAllocateVirtualMemory", func_NtAllocateVirtualMemory }, + { "NtApphelpCacheControl", func_NtApphelpCacheControl }, { "NtContinue", func_NtContinue }, { "NtCreateFile", func_NtCreateFile }, { "NtCreateThread", func_NtCreateThread },