[APPHELP][APPHELP_APITEST] Partially implement SdbGetMatchingExe, SdbInitDatabase...
authorMark Jansen <mark.jansen@reactos.org>
Sat, 3 Sep 2016 16:20:52 +0000 (16:20 +0000)
committerMark Jansen <mark.jansen@reactos.org>
Sat, 3 Sep 2016 16:20:52 +0000 (16:20 +0000)
- SdbGetMatchingExe, SdbInitDatabase partial implementation by Mislav Blažević
- Tests by me, they pass on vista - win10. (Win2k3 has another struct layout)
- Move shared test functions to a common header
- SdbTagRefToTagID misses functionality related to multiple databases, but that is not supported anywhere yet.

svn path=/trunk/; revision=72549

reactos/dll/appcompat/apphelp/CMakeLists.txt
reactos/dll/appcompat/apphelp/apphelp.c
reactos/dll/appcompat/apphelp/apphelp.h
reactos/dll/appcompat/apphelp/apphelp.spec
reactos/dll/appcompat/apphelp/hsdb.c [new file with mode: 0644]
reactos/dll/appcompat/apphelp/sdbapi.c
rostests/apitests/apphelp/apphelp.c
rostests/apitests/apphelp/apphelp_apitest.h [new file with mode: 0644]
rostests/apitests/apphelp/data.c
rostests/apitests/apphelp/db.c
rostests/apitests/apphelp/layerapi.c

index b003b74..745b51a 100644 (file)
@@ -3,6 +3,7 @@ spec2def(apphelp.dll apphelp.spec ADD_IMPORTLIB)
 
 list(APPEND SOURCE
     apphelp.c
+    hsdb.c
     layer.c
     sdbapi.c
     sdbfileattr.c
index 6d21769..158433b 100644 (file)
@@ -40,7 +40,7 @@ void ApphelppInitDebugLevel(void)
 {
     UNICODE_STRING DebugKey, DebugValue;
     NTSTATUS Status;
-    ULONG NewLevel = 0;
+    ULONG NewLevel = SHIM_ERR;
     WCHAR Buffer[40];
 
     RtlInitUnicodeString(&DebugKey, L"SHIM_DEBUG_LEVEL");
index 6982965..09ff87f 100644 (file)
@@ -50,22 +50,58 @@ typedef struct tagATTRINFO {
     };
 } ATTRINFO, *PATTRINFO;
 
+#define SDB_MAX_SDBS 16
+#define SDB_MAX_EXES 16
+#define SDB_MAX_LAYERS 8
+
+/* Flags for adwExeFlags */
+#define SHIMREG_DISABLE_SHIM (0x00000001)
+#define SHIMREG_DISABLE_APPHELP (0x00000002)
+#define SHIMREG_APPHELP_NOUI (0x00000004)
+#define SHIMREG_APPHELP_CANCEL (0x10000000)
+#define SHIMREG_DISABLE_SXS (0x00000010)
+#define SHIMREG_DISABLE_LAYER (0x00000020)
+#define SHIMREG_DISABLE_DRIVER (0x00000040)
+
+#define SDBGMEF_IGNORE_ENVIRONMENT (0x1)
+
+typedef struct tagSDBQUERYRESULT {
+    TAGREF atrExes[SDB_MAX_EXES];
+    DWORD  adwExeFlags[SDB_MAX_EXES];
+    TAGREF atrLayers[SDB_MAX_LAYERS];
+    DWORD  dwLayerFlags;
+    TAGREF trApphelp;
+    DWORD  dwExeCount;
+    DWORD  dwLayerCount;
+    GUID   guidID;
+    DWORD  dwFlags;
+    DWORD  dwCustomSDBMap;
+    GUID   rgGuidDB[SDB_MAX_SDBS];
+} SDBQUERYRESULT, *PSDBQUERYRESULT;
+
 /* apphelp.c */
 
 #include "sdbpapi.h"
 
 PWSTR SdbpStrDup(LPCWSTR string);
+HSDB WINAPI SdbInitDatabase(DWORD, LPCWSTR);
+void WINAPI SdbReleaseDatabase(HSDB);
 
 PDB WINAPI SdbOpenDatabase(LPCWSTR path, PATH_TYPE type);
 void WINAPI SdbCloseDatabase(PDB);
 BOOL WINAPI SdbIsNullGUID(CONST GUID *Guid);
+BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size);
+LPWSTR WINAPI SdbGetStringTagPtr(PDB db, TAGID tagid);
 
 /* sdbread.c */
 BOOL WINAPI SdbpReadData(PDB db, PVOID dest, DWORD offset, DWORD num);
 TAG WINAPI SdbGetTagFromTagID(PDB db, TAGID tagid);
 TAGID WINAPI SdbFindFirstTag(PDB db, TAGID parent, TAG tag);
+TAGID WINAPI SdbFindNextTag(PDB db, TAGID parent, TAGID prev_child);
 BOOL WINAPI SdbGetDatabaseID(PDB db, GUID* Guid);
 
+/* sdbfileattr.c*/
+BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info);
 
 /* layer.c */
 BOOL WINAPI AllowPermLayer(PCWSTR path);
index 2e94be5..095e952 100644 (file)
@@ -71,7 +71,7 @@
 @ stub SdbGetLayerName
 @ stub SdbGetLayerTagRef
 @ stub SdbGetLocalPDB
-@ stub SdbGetMatchingExe
+@ stdcall SdbGetMatchingExe(ptr wstr wstr wstr long ptr)
 @ stub SdbGetMsiPackageInformation
 @ stub SdbGetNamedLayer
 @ stdcall SdbGetNextChild(ptr long long)
@@ -87,7 +87,7 @@
 @ stub SdbGrabMatchingInfoEx
 @ stdcall SdbGUIDFromString(wstr ptr)
 @ stdcall SdbGUIDToString(ptr wstr long)
-@ stub SdbInitDatabase
+@ stdcall SdbInitDatabase(long wstr)
 @ stub SdbInitDatabaseEx
 @ stdcall SdbIsNullGUID(ptr)
 @ stub SdbIsStandardDatabase
 @ stdcall SdbReadWORDTag(ptr long long)
 @ stub SdbReadWORDTagRef
 @ stub SdbRegisterDatabase
-@ stub SdbReleaseDatabase
+@ stdcall SdbReleaseDatabase(ptr)
 @ stub SdbReleaseMatchingExe
 @ stub SdbResolveDatabase
 @ stub SdbSetApphelpDebugParameters
 @ stub SdbStringReplace
 @ stub SdbStringReplaceArray
 @ stub SdbTagIDToTagRef
-@ stub SdbTagRefToTagID
+@ stdcall SdbTagRefToTagID(ptr long ptr ptr)
 @ stdcall SdbTagToString(long)
 @ stub SdbUnregisterDatabase
 @ stdcall SdbWriteBinaryTag(ptr long ptr long)
diff --git a/reactos/dll/appcompat/apphelp/hsdb.c b/reactos/dll/appcompat/apphelp/hsdb.c
new file mode 100644 (file)
index 0000000..18c228f
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2011 André Hentschel
+ * Copyright 2013 Mislav Blažević
+ * Copyright 2015,2016 Mark Jansen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WIN32_NO_STATUS
+#include "windows.h"
+#include "ntndk.h"
+#include "strsafe.h"
+#include "apphelp.h"
+//#include "sdbstringtable.h"
+
+#include "wine/unicode.h"
+
+
+static BOOL WINAPI SdbpFileExists(LPCWSTR path)
+{
+    DWORD attr = GetFileAttributesW(path);
+    return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
+}
+
+
+/**
+ * Opens specified shim database file Handle returned by this function may only be used by
+ * functions which take HSDB param thus differing it from SdbOpenDatabase.
+ *
+ * @param [in]  flags   Specifies type of path or predefined database.
+ * @param [in]  path    Path to the shim database file.
+ *
+ * @return  Success: Handle to the opened shim database, NULL otherwise.
+ */
+HSDB WINAPI SdbInitDatabase(DWORD flags, LPCWSTR path)
+{
+    static const WCHAR shim[] = {'\\','s','y','s','m','a','i','n','.','s','d','b',0};
+    static const WCHAR msi[] = {'\\','m','s','i','m','a','i','n','.','s','d','b',0};
+    static const WCHAR drivers[] = {'\\','d','r','v','m','a','i','n','.','s','d','b',0};
+    LPCWSTR name;
+    WCHAR buffer[128];
+    HSDB sdb;
+
+    sdb = SdbAlloc(sizeof(SDB));
+    if (!sdb)
+        return NULL;
+    sdb->auto_loaded = 0;
+
+    /* Check for predefined databases */
+    if ((flags & HID_DATABASE_TYPE_MASK) && path == NULL)
+    {
+        switch (flags & HID_DATABASE_TYPE_MASK)
+        {
+            case SDB_DATABASE_MAIN_SHIM: name = shim; break;
+            case SDB_DATABASE_MAIN_MSI: name = msi; break;
+            case SDB_DATABASE_MAIN_DRIVERS: name = drivers; break;
+            default: return NULL;
+        }
+        SdbGetAppPatchDir(NULL, buffer, 128);
+        memcpy(buffer + lstrlenW(buffer), name, SdbpStrlen(name));
+    }
+
+    sdb->db = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1);
+
+    /* If database could not be loaded, a handle doesn't make sense either */
+    if (!sdb->db)
+    {
+        SdbReleaseDatabase(sdb);
+        return NULL;
+    }
+
+    return sdb;
+}
+
+/**
+ * Closes shim database opened by SdbInitDatabase.
+ *
+ * @param [in]  hsdb    Handle to the shim database.
+ */
+void WINAPI SdbReleaseDatabase(HSDB hsdb)
+{
+    SdbCloseDatabase(hsdb->db);
+    SdbFree(hsdb);
+}
+
+/**
+ * Queries database for a specified exe If hsdb is NULL default database shall be loaded and
+ * searched.
+ *
+ * @param [in]  hsdb        Handle to the shim database.
+ * @param [in]  path        Path to executable for which we query database.
+ * @param [in]  module_name Unused.
+ * @param [in]  env         Unused.
+ * @param [in]  flags       0 or SDBGMEF_IGNORE_ENVIRONMENT.
+ * @param [out] result      Pointer to structure in which query result shall be stored.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbGetMatchingExe(HSDB hsdb, LPCWSTR path, LPCWSTR module_name,
+                              LPCWSTR env, DWORD flags, PSDBQUERYRESULT result)
+{
+    static const WCHAR fmt[] = {'%','s','%','s',0};
+    BOOL ok, ret;
+    TAGID database, iter, attr;
+    PATTRINFO attribs = NULL;
+    /*DWORD attr_count;*/
+    LPWSTR file_name;
+    WCHAR dir_path[128];
+    WCHAR buffer[256];
+    PDB db;
+
+    /* Load default database if one is not specified */
+    if (!hsdb)
+    {
+        /* To reproduce windows behaviour HID_DOS_PATHS needs
+         * to be specified when loading default database */
+        hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
+        if(hsdb)
+            hsdb->auto_loaded = TRUE;
+    }
+
+    /* No database could be loaded */
+    if (!hsdb)
+        return FALSE;
+
+    db = hsdb->db;
+
+    /* Extract file name */
+    file_name = strrchrW(path, '\\') + 1;
+
+    /* Extract directory path */
+    memcpy(dir_path, path, (size_t)(file_name - path) * sizeof(WCHAR));
+
+    /* Get information about executable required to match it with database entry */
+    /*if (!SdbGetFileAttributes(path, &attribs, &attr_count))
+        return FALSE;*/
+
+    /* DATABASE is list TAG which contains all executables */
+    database = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
+    if (database == TAGID_NULL)
+        return FALSE;
+
+    /* EXE is list TAG which contains data required to match executable */
+    iter = SdbFindFirstTag(db, database, TAG_EXE);
+
+    /* Search for entry in database */
+    while (iter != TAGID_NULL)
+    {
+        /* Check if exe name matches */
+        attr = SdbFindFirstTag(db, iter, TAG_NAME);
+        if (lstrcmpiW(SdbGetStringTagPtr(db, attr), file_name) == 0)
+        {
+            /* Assume that entry is found (in case there are no "matching files") */
+            ok = TRUE;
+
+            /* Check if all "matching files" exist */
+            /* TODO: check size/checksum as well */
+            for (attr = SdbFindFirstTag(db, attr, TAG_MATCHING_FILE);
+                 attr != TAGID_NULL; attr = SdbFindNextTag(db, iter, attr))
+            {
+                snprintfW(buffer, 256, fmt, dir_path, SdbGetStringTagPtr(db, attr));
+                if (!SdbpFileExists(buffer))
+                    ok = FALSE;
+            }
+
+            /* Found it! */
+            if (ok)
+            {
+                /* TODO: fill result data */
+                /* TODO: there may be multiple matches */
+                ret = TRUE;
+                goto cleanup;
+            }
+        }
+
+        /* Continue iterating */
+        iter = SdbFindNextTag(db, database, iter);
+    }
+
+    /* Exe not found */
+    ret = FALSE;
+
+cleanup:
+    SdbFreeFileAttributes(attribs);
+    if (hsdb->auto_loaded) SdbReleaseDatabase(hsdb);
+    return ret;
+}
+
+/**
+ * Retrieves AppPatch directory.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [out] path    Pointer to memory in which path shall be written.
+ * @param [in]  size    Size of the buffer in characters.
+ */
+BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size)
+{
+    static WCHAR* default_dir = NULL;
+    static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0};
+
+    /* In case function fails, path holds empty string */
+    if (size > 0)
+        *path = 0;
+
+    if (!default_dir)
+    {
+        WCHAR* tmp = NULL;
+        UINT len = GetSystemWindowsDirectoryW(NULL, 0) + lstrlenW(szAppPatch);
+        tmp = SdbAlloc((len + 1)* sizeof(WCHAR));
+        if (tmp)
+        {
+            UINT r = GetSystemWindowsDirectoryW(tmp, len+1);
+            if (r && r < len)
+            {
+                if (SUCCEEDED(StringCchCatW(tmp, len+1, szAppPatch)))
+                {
+                    if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL)
+                        tmp = NULL;
+                }
+            }
+            if (tmp)
+                SdbFree(tmp);
+        }
+        if (!default_dir)
+        {
+            SHIM_ERR("Unable to obtain default AppPatch directory\n");
+            return FALSE;
+        }
+    }
+
+    if (!db)
+    {
+        return SUCCEEDED(StringCchCopyW(path, size, default_dir));
+    }
+    else
+    {
+        SHIM_ERR("Unimplemented for db != NULL\n");
+        return FALSE;
+    }
+}
+
+
+/**
+ * Translates the given trWhich to a specific database / tagid
+ *
+ * @param [in]      hsdb        Handle to the database.
+ * @param [in]      trWhich     Path to executable for which we query database.
+ * @param [out,opt] ppdb        The Shim database that trWhich belongs to.
+ * @param [out,opt] ptiWhich    The tagid that trWhich corresponds to.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbTagRefToTagID(HSDB hSDB, TAGREF trWhich, PDB* ppdb, TAGID* ptiWhich)
+{
+    if (trWhich & 0xf0000000)
+    {
+        SHIM_ERR("Multiple shim databases not yet implemented!\n");
+        return FALSE;
+    }
+
+    /* There seems to be no range checking on trWhich.. */
+    if (ppdb)
+        *ppdb = hSDB->db;
+    if (ptiWhich)
+        *ptiWhich = trWhich & 0x0fffffff;
+
+    return TRUE;
+}
+
index fd42577..04a5662 100644 (file)
@@ -460,59 +460,6 @@ void WINAPI SdbCloseDatabase(PDB db)
     SdbFree(db);
 }
 
-/**
- * Retrieves AppPatch directory.
- *
- * @param [in]  db      Handle to the shim database.
- * @param [out] path    Pointer to memory in which path shall be written.
- * @param [in]  size    Size of the buffer in characters.
- */
-BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size)
-{
-    static WCHAR* default_dir = NULL;
-    static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0};
-
-    /* In case function fails, path holds empty string */
-    if (size > 0)
-        *path = 0;
-
-    if (!default_dir)
-    {
-        WCHAR* tmp = NULL;
-        UINT len = GetSystemWindowsDirectoryW(NULL, 0) + lstrlenW(szAppPatch);
-        tmp = SdbAlloc((len + 1)* sizeof(WCHAR));
-        if (tmp)
-        {
-            UINT r = GetSystemWindowsDirectoryW(tmp, len+1);
-            if (r && r < len)
-            {
-                if (SUCCEEDED(StringCchCatW(tmp, len+1, szAppPatch)))
-                {
-                    if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL)
-                        tmp = NULL;
-                }
-            }
-            if (tmp)
-                SdbFree(tmp);
-        }
-        if (!default_dir)
-        {
-            SHIM_ERR("Unable to obtain default AppPatch directory\n");
-            return FALSE;
-        }
-    }
-
-    if (!db)
-    {
-        return SUCCEEDED(StringCchCopyW(path, size, default_dir));
-    }
-    else
-    {
-        /* fixme */
-        return FALSE;
-    }
-}
-
 /**
  * Parses a string to retrieve a GUID.
  *
index 8ba1fd9..d17afdc 100644 (file)
 
 #include "wine/test.h"
 
-/* data.c */
-void test_create_exe_imp(const char* name, int skip_rsrc_exports);
-void test_create_file_imp(const char* name, const char* contents, size_t len);
-void test_create_ne_imp(const char* name, int skip_names);
-DWORD get_host_winver(void);
-
-#define test_create_exe     (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_exe_imp
-#define test_create_file    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_file_imp
-#define test_create_ne      (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_ne_imp
-
-
-static DWORD g_WinVersion;
-
-#define WINVER_ANY     0
-#define WINVER_WINXP   0x0501
-#define WINVER_2003    0x0502
-#define WINVER_VISTA   0x0600
-#define WINVER_WIN7    0x0601
-#define WINVER_WIN8    0x0602
-#define WINVER_WIN10   0x1000
+#include "apphelp_apitest.h"
 
 
 typedef WORD TAG;
@@ -848,6 +829,8 @@ START_TEST(apphelp)
 {
     g_WinVersion = get_host_winver();
     trace("Detected version: 0x%x\n", g_WinVersion);
+    silence_debug_output();
+
     //SetEnvironmentVariable("SHIM_DEBUG_LEVEL", "4");
     //SetEnvironmentVariable("DEBUGCHANNEL", "+apphelp");
     hdll = LoadLibraryA("apphelp.dll");
diff --git a/rostests/apitests/apphelp/apphelp_apitest.h b/rostests/apitests/apphelp/apphelp_apitest.h
new file mode 100644 (file)
index 0000000..f0ec667
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef APPHELP_APITEST_H
+#define APPHELP_APITEST_H
+
+
+/* data.c */
+void test_create_db_imp(const char* name);
+DWORD test_get_db_size();
+void test_create_exe_imp(const char* name, int skip_rsrc_exports);
+void test_create_file_imp(const char* name, const char* contents, size_t len);
+void test_create_ne_imp(const char* name, int skip_names);
+DWORD get_host_winver(void);
+void silence_debug_output(void);        // Silence output if the environment variable is not set.
+
+#define test_create_db      (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_db_imp
+#define test_create_exe     (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_exe_imp
+#define test_create_file    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_file_imp
+#define test_create_ne      (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_ne_imp
+
+
+static DWORD g_WinVersion;
+
+#define WINVER_ANY     0
+#define WINVER_WINXP   0x0501
+#define WINVER_2003    0x0502
+#define WINVER_VISTA   0x0600
+#define WINVER_WIN7    0x0601
+#define WINVER_WIN8    0x0602
+#define WINVER_WIN10   0x0a00
+
+
+#endif // APPHELP_APITEST_H
index 7e19c51..20296e8 100644 (file)
@@ -771,11 +771,16 @@ static unsigned char rawData[2356] = {
     0x21, 0x00, 0x00, 0x00
 };
 
+DWORD test_get_db_size()
+{
+    return sizeof(rawData);
+}
+
 void test_create_db_imp(const char* name)
 {
     HANDLE file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-    ok(file != INVALID_HANDLE_VALUE, "can't create file\n");
-    if(file != INVALID_HANDLE_VALUE)
+    winetest_ok(file != INVALID_HANDLE_VALUE, "can't create file '%s'\n", name);
+    if (file != INVALID_HANDLE_VALUE)
     {
         DWORD size;
         WriteFile(file, rawData, sizeof(rawData), &size, NULL);
@@ -799,3 +804,11 @@ DWORD get_host_winver(void)
     return g_WinVersion;
 }
 
+void silence_debug_output(void)
+{
+    if (GetEnvironmentVariableA("SHIM_DEBUG_LEVEL", NULL, 0) == ERROR_ENVVAR_NOT_FOUND)
+        SetEnvironmentVariableA("SHIM_DEBUG_LEVEL", "0");
+    if (GetEnvironmentVariableA("SHIMENG_DEBUG_LEVEL", NULL, 0) == ERROR_ENVVAR_NOT_FOUND)
+        SetEnvironmentVariableA("SHIMENG_DEBUG_LEVEL", "0");
+}
+
index 503b8f2..aa8697b 100644 (file)
 
 #include "wine/test.h"
 
-/* data.c */
-void test_create_db_imp(const char* name);
+#include "apphelp_apitest.h"
+
 
-#define test_create_db      (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_create_db_imp
 
 typedef WORD TAG;
 typedef DWORD TAGID;
 typedef DWORD TAGREF;
 typedef UINT64 QWORD;
 typedef VOID* PDB;
+typedef VOID* HSDB;
 typedef INT PATH_TYPE;
+
 #define DOS_PATH 0
+#define HID_DATABASE_FULLPATH 2
+
+#define SDB_DATABASE_MAIN_SHIM 0x80030000
+
 
 #define TAGID_NULL 0x0
 #define TAGID_ROOT 0x0
@@ -134,6 +139,26 @@ typedef INT PATH_TYPE;
 #define TAG_DATABASE_ID (0x7 | TAG_TYPE_BINARY)
 
 
+#define SDB_MAX_SDBS 16
+#define SDB_MAX_EXES 16
+#define SDB_MAX_LAYERS 8
+
+#define SDBQUERYRESULT_EXPECTED_SIZE    456
+
+typedef struct tagSDBQUERYRESULT {
+    TAGREF atrExes[SDB_MAX_EXES];
+    DWORD  adwExeFlags[SDB_MAX_EXES];
+    TAGREF atrLayers[SDB_MAX_LAYERS];
+    DWORD  dwLayerFlags;
+    TAGREF trApphelp;
+    DWORD  dwExeCount;
+    DWORD  dwLayerCount;
+    GUID   guidID;
+    DWORD  dwFlags;
+    DWORD  dwCustomSDBMap;
+    GUID   rgGuidDB[SDB_MAX_SDBS];
+} SDBQUERYRESULT, *PSDBQUERYRESULT;
+
 
 static HMODULE hdll;
 static LPCWSTR (WINAPI *pSdbTagToString)(TAG);
@@ -166,6 +191,10 @@ static TAGID (WINAPI *pSdbGetFirstChild)(PDB, TAGID);
 static TAGID (WINAPI *pSdbGetNextChild)(PDB, TAGID, TAGID);
 static BOOL (WINAPI *pSdbGetDatabaseID)(PDB, GUID*);
 static BOOL (WINAPI *pSdbGUIDToString)(CONST GUID *, PCWSTR, SIZE_T);
+static HSDB (WINAPI *pSdbInitDatabase)(DWORD, LPCWSTR);
+static void (WINAPI *pSdbReleaseDatabase)(HSDB);
+static BOOL(WINAPI *pSdbGetMatchingExe)(HSDB hsdb, LPCWSTR path, LPCWSTR module_name, LPCWSTR env, DWORD flags, PSDBQUERYRESULT result);
+static BOOL (WINAPI *pSdbTagRefToTagID)(HSDB hSDB, TAGREF trWhich, PDB *ppdb, TAGID *ptiWhich);
 static LONGLONG(WINAPI* pSdbMakeIndexKeyFromString)(LPCWSTR);
 
 
@@ -717,6 +746,9 @@ static void check_matching_file(PDB pdb, TAGID exe, TAGID matching_file, int num
     }
 }
 
+// "C:\WINDOWS\system32\pcaui.exe" /g {bf39e0e6-c61c-4a22-8802-3ea8ad00b655} /x {4e50c93f-b863-4dfa-bae2-d80ef4ce5c89} /a "apphelp_name_allow" /v "apphelp_vendor_allow" /s "Allow it!" /b 1 /f 0 /k 0 /e "C:\Users\Mark\AppData\Local\Temp\apphelp_test\test_allow.exe" /u "http://reactos.org/allow" /c
+// "C:\WINDOWS\system32\pcaui.exe" /g {fa150915-1244-4169-a4ba-fc098c442840} /x {156720e1-ef98-4d04-965a-d85de05e6d9f} /a "apphelp_name_disallow" /v "apphelp_vendor_disallow" /s "Not allowed!" /b 2 /f 0 /k 0 /e "C:\Users\Mark\AppData\Local\Temp\apphelp_test\test_disallow.exe" /u "http://reactos.org/disallow" /c
+
 static void check_matching_apphelp(PDB pdb, TAGID apphelp, int num)
 {
     if(num == 0)
@@ -789,8 +821,11 @@ static struct
     const char* extra_file;
     DWORD dwLayerCount;
     TAGREF atrExes_0;
+    DWORD adwExeFlags_0;
     TAGREF atrLayers_0;
-} test_exedata[4] = {
+    TAGREF trApphelp;
+    const char* env_var;
+} test_exedata[5] = {
     {
         "test_allow.exe",
         "apphelp_name_allow",
@@ -799,7 +834,10 @@ static struct
         NULL,
         0,
         0x1c6,
+        0x1000,
         0,
+        0x1c6,
+        NULL,
     },
     {
         "test_disallow.exe",
@@ -809,7 +847,10 @@ static struct
         NULL,
         0,
         0x256,
+        0x3000,
         0,
+        0x256,
+        NULL,
     },
     {
         "test_new.exe",
@@ -819,7 +860,10 @@ static struct
         "test_checkfile.txt",
         1,
         0x2ec,
+        0,
         0x18e,
+        0,
+        NULL,
     },
     {
         "test_w2k3.exe",
@@ -830,6 +874,22 @@ static struct
         0,
         0x37c,
         0,
+        0,
+        0,
+        NULL,
+    },
+    {
+        "test_unknown_file.exe",
+        "apphelp_name_allow",
+        "apphelp_vendor_allow",
+        {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+        NULL,
+        1,
+        0,
+        0,
+        0x18e,
+        0,
+        "TestNewMode",
     },
 };
 
@@ -968,6 +1028,314 @@ static void test_CheckDatabaseManually(void)
     DeleteFileA("test_db.sdb");
 }
 
+static void test_is_testdb(PDB pdb)
+{
+    if(pdb)
+    {
+        GUID guid;
+        memset(&guid, 0, sizeof(guid));
+        ok(pSdbGetDatabaseID(pdb, &guid),"expected SdbGetDatabaseID not to fail.\n");
+        ok(IsEqualGUID(&guid, &GUID_DATABASE_TEST), "Expected SdbGetDatabaseID to return the test db GUID, was: %s\n", wine_dbgstr_guid(&guid));
+    }
+    else
+    {
+        skip("Not checking DB GUID, received a null pdb\n");
+    }
+}
+
+
+static void test_mode_generic(const char* workdir, HSDB hsdb, int cur)
+{
+    char exename[MAX_PATH], testfile[MAX_PATH];
+    WCHAR exenameW[MAX_PATH];
+    BOOL ret;
+    SDBQUERYRESULT query;
+    PDB pdb;
+    TAGID tagid;
+    TAGREF trApphelp;
+    DWORD expect_flags = 0, adwExeFlags_0, exe_count;
+    int n;
+
+    memset(&query, 0xab, sizeof(query));
+
+    sprintf(exename, "%s\\%s", workdir, test_exedata[cur].name);
+    if (test_exedata[cur].extra_file)
+        sprintf(testfile, "%s\\%s", workdir, test_exedata[cur].extra_file);
+    test_create_exe(exename, 0);
+    MultiByteToWideChar(CP_ACP, 0, exename, -1, exenameW, MAX_PATH);
+
+    if (test_exedata[cur].extra_file)
+    {
+        /* First we try without the file at all. */
+        DeleteFileA(testfile);
+        ret = pSdbGetMatchingExe(hsdb, exenameW, NULL, NULL, 0, &query);
+        ok(ret == 0, "SdbGetMatchingExe should have failed for %d.\n", cur);
+        /* Now re-try with the correct file */
+        test_create_file(testfile, "aaaa", 4);
+    }
+
+#if 0
+    // Results seem to be cached based on filename, until we can invalidate this, do not test the same filename twice!
+    DeleteFileA(exename);
+    // skip exports
+    test_create_exe(exename, 1);
+    ret = pSdbGetMatchingExe(hsdb, exenameW, NULL, NULL, 0, &query);
+    ok(ret == 0, "SdbGetMatchingExe should have failed for %d.\n", cur);
+
+    DeleteFileA(exename);
+    test_create_exe(exename, 0);
+#endif
+
+    if (test_exedata[cur].env_var)
+    {
+        SetEnvironmentVariableA("__COMPAT_LAYER", test_exedata[cur].env_var);
+    }
+
+    ret = pSdbGetMatchingExe(hsdb, exenameW, NULL, NULL, 0, &query);
+    ok(ret, "SdbGetMatchingExe should not fail for %d.\n", cur);
+
+    exe_count = (test_exedata[cur].env_var == NULL) ? 1 : 0;
+
+    ok(query.dwExeCount == exe_count, "Expected dwExeCount to be %d, was %d for %d\n", exe_count, query.dwExeCount, cur);
+    ok(query.dwLayerCount == test_exedata[cur].dwLayerCount, "Expected dwLayerCount to be %d, was %d for %d\n", test_exedata[cur].dwLayerCount, query.dwLayerCount, cur);
+    ok(query.dwCustomSDBMap == 1, "Expected dwCustomSDBMap to be 1, was %d for %d\n", query.dwCustomSDBMap, cur);
+    ok(query.dwLayerFlags == 0, "Expected dwLayerFlags to be 0, was 0x%x for %d\n", query.dwLayerFlags, cur);
+    trApphelp = (g_WinVersion < WINVER_WIN10) ? 0 : test_exedata[cur].trApphelp;
+    ok(query.trApphelp == trApphelp, "Expected trApphelp to be 0x%x, was 0x%x for %d\n", trApphelp, query.trApphelp, cur);
+
+    if (g_WinVersion < WINVER_WIN7)
+        expect_flags = 0;
+    else if (g_WinVersion < WINVER_WIN8)
+        expect_flags = 1;
+    else if (g_WinVersion < WINVER_WIN10)
+        expect_flags = 0x101;
+    else
+        expect_flags = 0x121;
+
+    if (test_exedata[cur].env_var)
+        expect_flags &= ~0x100;
+
+    ok(query.dwFlags == expect_flags, "Expected dwFlags to be 0x%x, was 0x%x for %d\n", expect_flags, query.dwFlags, cur);
+
+    ok(query.atrExes[0] == test_exedata[cur].atrExes_0, "Expected atrExes[0] to be 0x%x, was: 0x%x for %d\n", test_exedata[cur].atrExes_0, query.atrExes[0], cur);
+    for (n = 1; n < SDB_MAX_EXES; ++n)
+        ok(query.atrExes[n] == 0, "Expected atrExes[%d] to be 0, was: %x for %d\n", n, query.atrExes[n], cur);
+
+    adwExeFlags_0 = (g_WinVersion < WINVER_WIN10) ? 0 : test_exedata[cur].adwExeFlags_0;
+    ok(query.adwExeFlags[0] == adwExeFlags_0, "Expected adwExeFlags[0] to be 0x%x, was: 0x%x for %d\n", adwExeFlags_0, query.adwExeFlags[0], cur);
+    for (n = 1; n < SDB_MAX_EXES; ++n)
+        ok(query.adwExeFlags[n] == 0, "Expected adwExeFlags[%d] to be 0, was: %x for %d\n", n, query.adwExeFlags[0], cur);
+
+    ok(query.atrLayers[0] == test_exedata[cur].atrLayers_0, "Expected atrLayers[0] to be 0x%x, was: %x for %d\n", test_exedata[cur].atrLayers_0, query.atrLayers[0], cur);
+    for (n = 1; n < SDB_MAX_LAYERS; ++n)
+        ok(query.atrLayers[n] == 0, "Expected atrLayers[%d] to be 0, was: %x for %d\n", n, query.atrLayers[0], cur);
+
+    ok(IsEqualGUID(&query.rgGuidDB[0], &GUID_DATABASE_TEST), "Expected rgGuidDB[0] to be the test db GUID, was: %s for %d\n", wine_dbgstr_guid(&query.rgGuidDB[0]), cur);
+    for (n = 1; n < SDB_MAX_SDBS; ++n)
+        ok(IsEqualGUID(&query.rgGuidDB[n], &GUID_NULL), "Expected rgGuidDB[%d] to be GUID_NULL, was: %s for %d\n", n, wine_dbgstr_guid(&query.rgGuidDB[n]), cur);
+
+    if (query.atrExes[0])
+    {
+        pdb = (PDB)0x12345678;
+        tagid = 0x76543210;
+        ret = pSdbTagRefToTagID(hsdb, query.atrExes[0], &pdb, &tagid);
+        ok(ret, "SdbTagRefToTagID failed for %d.\n", cur);
+        ok(pdb != NULL && pdb != (PDB)0x12345678, "SdbTagRefToTagID failed to return a pdb for %d.\n", cur);
+        ok(tagid != 0 && tagid != 0x76543210, "SdbTagRefToTagID failed to return a tagid for %d.\n", cur);
+
+        if (pdb && pdb != (PDB)0x12345678)
+        {
+            TAG tag = pSdbGetTagFromTagID(pdb, tagid);
+            test_is_testdb(pdb);
+            ok(tag == TAG_EXE, "Expected tag to be TAG_EXE, was 0x%x for %d.\n", tag, cur);
+            match_str_attr(pdb, tagid, TAG_NAME, test_exedata[cur].name);
+        }
+        else
+        {
+            skip("Skipping a bunch of tests because of an invalid pointer\n");
+        }
+    }
+
+    if (test_exedata[cur].atrLayers_0)
+    {
+        pdb = (PDB)0x12345678;
+        tagid = 0x76543210;
+        ret = pSdbTagRefToTagID(hsdb, query.atrLayers[0], &pdb, &tagid);
+        ok(ret, "SdbTagRefToTagID failed for %d.\n", cur);
+        ok(pdb != NULL && pdb != (PDB)0x12345678, "SdbTagRefToTagID failed to return a pdb for %d.\n", cur);
+        ok(tagid != 0 && tagid != 0x76543210, "SdbTagRefToTagID failed to return a tagid for %d.\n", cur);
+
+        if (pdb && pdb != (PDB)0x12345678)
+        {
+            TAG tag = pSdbGetTagFromTagID(pdb, tagid);
+            test_is_testdb(pdb);
+            ok(tag == TAG_LAYER, "Expected tag to be TAG_LAYER, was 0x%x for %d.\n", tag, cur);
+            match_str_attr(pdb, tagid, TAG_NAME, "TestNewMode");
+        }
+        else
+        {
+            skip("Skipping a bunch of tests because of an invalid pointer\n");
+        }
+    }
+
+    pdb = (PDB)0x12345678;
+    tagid = 0x76543210;
+    ret = pSdbTagRefToTagID(hsdb, 0, &pdb, &tagid);
+    ok(pdb != NULL && pdb != (PDB)0x12345678, "Expected pdb to be set to a valid pdb, was: %p\n", pdb);
+    ok(tagid == 0, "Expected tagid to be set to 0, was: 0x%x\n", tagid);
+
+    if (test_exedata[cur].extra_file)
+        DeleteFileA(testfile);
+    DeleteFileA(exename);
+
+    if (test_exedata[cur].env_var)
+    {
+        SetEnvironmentVariableA("__COMPAT_LAYER", NULL);
+    }
+}
+
+
+static void test_MatchApplications(void)
+{
+    char workdir[MAX_PATH], dbpath[MAX_PATH];
+    WCHAR dbpathW[MAX_PATH];
+    BOOL ret;
+    HSDB hsdb;
+
+    ret = GetTempPathA(MAX_PATH, workdir);
+    ok(ret, "GetTempPathA error: %d\n", GetLastError());
+    lstrcatA(workdir, "apphelp_test");
+
+    ret = CreateDirectoryA(workdir, NULL);
+    ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
+
+    /* SdbInitDatabase needs an nt-path */
+    sprintf(dbpath, "\\??\\%s\\test.sdb", workdir);
+
+    test_create_db(dbpath + 4);
+
+    MultiByteToWideChar(CP_ACP, 0, dbpath, -1, dbpathW, MAX_PATH);
+    hsdb = pSdbInitDatabase(HID_DATABASE_FULLPATH, dbpathW);
+
+    ok(hsdb != NULL, "Expected a valid database handle\n");
+
+    if (!hsdb)
+    {
+        skip("SdbInitDatabase not implemented?\n");
+    }
+    else
+    {
+        /* now that our enviroment is setup, let's go ahead and run the actual tests.. */
+        int n;
+        for (n = 0; n < _countof(test_exedata); ++n)
+            test_mode_generic(workdir, hsdb, n);
+        pSdbReleaseDatabase(hsdb);
+    }
+
+    DeleteFileA(dbpath + 4);
+
+    ret = RemoveDirectoryA(workdir);
+    ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
+}
+
+static void test_TagRef(void)
+{
+    char tmpdir[MAX_PATH], dbpath[MAX_PATH];
+    WCHAR dbpathW[MAX_PATH];
+    BOOL ret;
+    HSDB hsdb;
+    PDB pdb;
+    TAGID db;
+    DWORD size;
+
+    ret = GetTempPathA(MAX_PATH, tmpdir);
+    ok(ret, "GetTempPathA error: %d\n", GetLastError());
+
+    /* SdbInitDatabase needs an nt-path */
+    sprintf(dbpath, "\\??\\%stest.sdb", tmpdir);
+
+    test_create_db(dbpath + 4);
+
+    MultiByteToWideChar(CP_ACP, 0, dbpath, -1, dbpathW, MAX_PATH);
+    hsdb = pSdbInitDatabase(HID_DATABASE_FULLPATH, dbpathW);
+
+    /* HSDB is the only arg that can't be null */
+    ret = pSdbTagRefToTagID(hsdb, 0, NULL, NULL);
+    ok(ret == TRUE, "Expected ret to be TRUE, was: %d\n", ret);
+
+    size = test_get_db_size();
+
+    pdb = (PDB)&db;
+    db = 12345;
+    ret = pSdbTagRefToTagID(hsdb, size - 1, &pdb, &db);
+    ok(ret == TRUE, "Expected ret to be TRUE, was: %d\n", ret);
+    ok(pdb != NULL, "Expected a result, got: %p\n", pdb);
+    ok(db == (size - 1), "Expected %u, got: %u\n", size - 1, db);
+
+    pdb = (PDB)&db;
+    db = 12345;
+    ret = pSdbTagRefToTagID(hsdb, size, &pdb, &db);
+    ok(ret == TRUE, "Expected ret to be TRUE, was: %d\n", ret);
+    ok(pdb != NULL, "Expected a result, got: %p\n", pdb);
+    ok(db == (size), "Expected %u, got: %u\n", size, db);
+
+    pdb = (PDB)&db;
+    db = 12345;
+    ret = pSdbTagRefToTagID(hsdb, size + 1, &pdb, &db);
+    ok(ret == TRUE, "Expected ret to be TRUE, was: %d\n", ret);
+    ok(pdb != NULL, "Expected a result, got: %p\n", pdb);
+    ok(db == (size + 1), "Expected %u, got: %u\n", size + 1, db);
+
+    pdb = (PDB)&db;
+    db = 12345;
+    ret = pSdbTagRefToTagID(hsdb, 0x0fffffff, &pdb, &db);
+    ok(ret == TRUE, "Expected ret to be TRUE, was: %d\n", ret);
+    ok(pdb != NULL, "Expected a result, got: %p\n", pdb);
+    ok(db == 0x0fffffff, "Expected %u, got: %u\n", 0x0fffffff, db);
+
+    pdb = (PDB)&db;
+    db = 12345;
+    ret = pSdbTagRefToTagID(hsdb, 0x10000000, &pdb, &db);
+    ok(ret == FALSE, "Expected ret to be FALSE, was: %d\n", ret);
+    ok(pdb == NULL, "Expected no result, got: %p\n", pdb);
+    ok(db == 0, "Expected no result, got: 0x%x\n", db);
+
+    pdb = NULL;
+    db = TAGID_NULL;
+    ret = pSdbTagRefToTagID(hsdb, TAGID_ROOT, &pdb, NULL);
+    ok(ret != FALSE, "Expected ret to be TRUE, was: %d\n", ret);
+    ok(pdb != NULL, "Expected pdb to be valid\n");
+
+    if(pdb == NULL)
+    {
+        skip("Cannot run tests without pdb\n");
+    }
+    else
+    {
+        db = pSdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
+        if(db != TAGID_NULL)
+        {
+            TAGID child;
+            child = pSdbGetFirstChild(pdb, db);
+            while(child != TAGID_NULL)
+            {
+                PDB pdb_res;
+                TAGID tagid_res;
+                ret = pSdbTagRefToTagID(hsdb, child, &pdb_res, &tagid_res);
+                ok(ret, "Expected SdbTagRefToTagID to succeed\n");
+                child = pSdbGetNextChild(pdb, db, child);
+            }
+        }
+        else
+        {
+            skip("Cannot run tests without valid db tag\n");
+        }
+    }
+
+    DeleteFileA(dbpath + 4);
+}
+
+
 
 static void expect_indexA_imp(const char* text, LONGLONG expected)
 {
@@ -1051,10 +1419,39 @@ static void test_IndexKeyFromString(void)
         wine_dbgstr_longlong(0xbc414044444444), wine_dbgstr_longlong(result));
 }
 
+static int validate_SDBQUERYRESULT_size()
+{
+    unsigned char buffer[SDBQUERYRESULT_EXPECTED_SIZE * 2];
+    WCHAR path[MAX_PATH];
+    HSDB hsdb;
+
+    memset(buffer, 0xab, sizeof(buffer));
+    hsdb = pSdbInitDatabase(SDB_DATABASE_MAIN_SHIM, NULL);
+    if (hsdb)
+    {
+        GetModuleFileNameW(NULL, path, MAX_PATH);
+        pSdbGetMatchingExe(hsdb, path, NULL, NULL, 0, (SDBQUERYRESULT*)buffer);
+        pSdbReleaseDatabase(hsdb);
+        if (buffer[0] == buffer[1] && buffer[1] == 0xab)
+        {
+            trace("SdbGetMatchingExe didnt do anything, cannot determine SDBQUERYRESULT size\n");
+            return 1;
+        }
+        return buffer[SDBQUERYRESULT_EXPECTED_SIZE] == 0xab && buffer[SDBQUERYRESULT_EXPECTED_SIZE-1] != 0xab;
+    }
+
+    return 1;
+}
+
 
 START_TEST(db)
 {
+    g_WinVersion = get_host_winver();
+    trace("Detected version: 0x%x\n", g_WinVersion);
+    silence_debug_output();
+
     //SetEnvironmentVariable("SHIM_DEBUG_LEVEL", "4");
+    //SetEnvironmentVariable("SHIMENG_DEBUG_LEVEL", "4");
     //SetEnvironmentVariable("DEBUGCHANNEL", "+apphelp");
     hdll = LoadLibraryA("apphelp.dll");
     pSdbTagToString = (void *) GetProcAddress(hdll, "SdbTagToString");
@@ -1087,11 +1484,21 @@ START_TEST(db)
     pSdbGetNextChild = (void *) GetProcAddress(hdll, "SdbGetNextChild");
     pSdbGetDatabaseID = (void *) GetProcAddress(hdll, "SdbGetDatabaseID");
     pSdbGUIDToString = (void *) GetProcAddress(hdll, "SdbGUIDToString");
-    pSdbMakeIndexKeyFromString = (void*) GetProcAddress(hdll, "SdbMakeIndexKeyFromString");
+    pSdbInitDatabase = (void *) GetProcAddress(hdll, "SdbInitDatabase");
+    pSdbReleaseDatabase = (void *) GetProcAddress(hdll, "SdbReleaseDatabase");
+    pSdbGetMatchingExe = (void *) GetProcAddress(hdll, "SdbGetMatchingExe");
+    pSdbTagRefToTagID = (void *) GetProcAddress(hdll, "SdbTagRefToTagID");
+    pSdbMakeIndexKeyFromString = (void *) GetProcAddress(hdll, "SdbMakeIndexKeyFromString");
 
     test_Sdb();
     test_write_ex();
     test_stringtable();
     test_CheckDatabaseManually();
+    if (validate_SDBQUERYRESULT_size())
+        test_MatchApplications();
+    else
+        skip("Skipping tests with SDBQUERYRESULT due to a wrong size reported\n");
+    test_TagRef();
+    skip("test_SecondaryDB()\n");
     test_IndexKeyFromString();
 }
index 636044c..2d43b08 100644 (file)
@@ -31,8 +31,7 @@
 
 #include "wine/test.h"
 
-/* data.c */
-DWORD get_host_winver(void);
+#include "apphelp_apitest.h"
 
 #define GPLK_USER 1
 #define GPLK_MACHINE 2
@@ -47,12 +46,6 @@ static BOOL(WINAPI *pSdbGetPermLayerKeys)(PCWSTR wszPath, PWSTR pwszLayers, PDWO
 static BOOL(WINAPI *pSetPermLayerState)(PCWSTR wszPath, PCWSTR wszLayer, DWORD dwFlags, BOOL bMachine, BOOL bEnable);
 
 
-static DWORD g_WinVersion;
-#define WINVER_VISTA   0x0600
-#define WINVER_WIN8    0x0602
-#define WINVER_WIN10   0x1000
-
-
 /* Helper function to disable Wow64 redirection on an os that reports it being enabled. */
 static DWORD g_QueryFlag = 0xffffffff;
 static DWORD QueryFlag(void)
@@ -877,6 +870,7 @@ static void test_Sign_Media(void)
 
 START_TEST(layerapi)
 {
+    silence_debug_output();
     /*SetEnvironmentVariable("SHIM_DEBUG_LEVEL", "4");*/
     hdll = LoadLibraryA("apphelp.dll");
     pAllowPermLayer = (void *)GetProcAddress(hdll, "AllowPermLayer");