[APPHELP][APPHELP_APITEST] Add SdbGetFileAttributes + tests, based on the work of...
authorMark Jansen <mark.jansen@reactos.org>
Sun, 1 May 2016 19:12:06 +0000 (19:12 +0000)
committerMark Jansen <mark.jansen@reactos.org>
Sun, 1 May 2016 19:12:06 +0000 (19:12 +0000)
- Implement SdbGetFileAttributes (based on the work of Mislav Blažević)
- Add tests for SdbGetFileAttributes

svn path=/trunk/; revision=71226

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

index e1beb19..58642ff 100644 (file)
@@ -3,8 +3,9 @@ spec2def(apphelp.dll apphelp.spec ADD_IMPORTLIB)
 
 list(APPEND SOURCE
     apphelp.c
-    sdbapi.c
     layer.c
+    sdbapi.c
+    sdbfileattr.c
     apphelp.spec
     apphelp.h
     ${CMAKE_CURRENT_BINARY_DIR}/apphelp_stubs.c)
@@ -15,6 +16,7 @@ add_library(apphelp SHARED
 
 set_module_type(apphelp win32dll)
 target_link_libraries(apphelp wine)
-#add_delay_importlibs(apphelp version imagehlp user32)
-add_importlibs(apphelp msvcrt kernel32 ntdll)
+# When binutils is fixed, we should move imagehlp to delay! CORE-6504
+add_delay_importlibs(apphelp version)
+add_importlibs(apphelp msvcrt imagehlp kernel32 ntdll)
 add_cd_file(TARGET apphelp DESTINATION reactos/system32 FOR all)
index 1665b7f..a0eda94 100644 (file)
@@ -32,6 +32,16 @@ typedef UINT64 QWORD;
 #define TAGREF_NULL (0)
 #define TAGREF_ROOT (0)
 
+typedef struct tagATTRINFO {
+  TAG   type;
+  DWORD flags;
+  union {
+    QWORD qwattr;
+    DWORD dwattr;
+    WCHAR *lpattr;
+  };
+} ATTRINFO, *PATTRINFO;
+
 typedef enum _SHIM_LOG_LEVEL {
     SHIM_ERR = 1,
     SHIM_WARN = 2,
@@ -72,6 +82,19 @@ void SdbpFree(LPVOID mem);
 
 #endif
 
+typedef struct tagMEMMAPPED {
+    HANDLE file;
+    HANDLE section;
+    PBYTE view;
+    SIZE_T size;
+    SIZE_T mapped_size;
+} MEMMAPPED, *PMEMMAPPED;
+
+BOOL WINAPI SdbpOpenMemMappedFile(LPCWSTR path, PMEMMAPPED mapping);
+void WINAPI SdbpCloseMemMappedFile(PMEMMAPPED mapping);
+DWORD SdbpStrlen(LPCWSTR string);
+PWSTR SdbpStrDup(LPCWSTR string);
+
 
 /* layer.c */
 BOOL WINAPI AllowPermLayer(PCWSTR path);
@@ -79,6 +102,9 @@ BOOL WINAPI SdbGetPermLayerKeys(PCWSTR wszPath, PWSTR pwszLayers, PDWORD pdwByte
 BOOL WINAPI SetPermLayerState(PCWSTR wszPath, PCWSTR wszLayer, DWORD dwFlags, BOOL bMachine, BOOL bEnable);
 
 
+#define ATTRIBUTE_AVAILABLE 0x1
+#define ATTRIBUTE_FAILED 0x2
+
 #define TAGID_NULL 0x0
 #define TAGID_ROOT 0x0
 
index 78f62c0..5323c9b 100644 (file)
@@ -48,7 +48,7 @@
 @ stub SdbFindNextTag
 @ stub SdbFindNextTagRef
 @ stub SdbFreeDatabaseInformation
-@ stub SdbFreeFileAttributes
+@ stdcall SdbFreeFileAttributes(ptr)
 @ stub SdbFreeFileInfo
 @ stub SdbFreeFlagInfo
 @ stub SdbGetAppCompatDataSize
@@ -61,7 +61,7 @@
 @ stub SdbGetDatabaseVersion
 @ stub SdbGetDllPath
 @ stub SdbGetEntryFlags
-@ stub SdbGetFileAttributes
+@ stdcall SdbGetFileAttributes(wstr ptr ptr)
 @ stub SdbGetFileImageType
 @ stub SdbGetFileImageTypeEx
 @ stub SdbGetFileInfo
index 5c99d37..fc6f2e3 100644 (file)
@@ -155,11 +155,6 @@ void SdbpHeapDeinit(void)
     HeapDestroy(g_Heap);
 }
 
-DWORD SdbpStrlen(PCWSTR string)
-{
-    return (lstrlenW(string) + 1) * sizeof(WCHAR);
-}
-
 static HANDLE SdbpHeap(void)
 {
     return g_Heap;
@@ -203,6 +198,94 @@ void SdbpFree(LPVOID mem
     HeapFree(SdbpHeap(), 0, mem);
 }
 
+DWORD SdbpStrlen(PCWSTR string)
+{
+    return (lstrlenW(string) + 1) * sizeof(WCHAR);
+}
+
+PWSTR SdbpStrDup(LPCWSTR string)
+{
+    PWSTR ret = SdbpAlloc(SdbpStrlen(string));
+    lstrcpyW(ret, string);
+    return ret;
+}
+
+
+BOOL WINAPI SdbpOpenMemMappedFile(LPCWSTR path, PMEMMAPPED mapping)
+{
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_STANDARD_INFORMATION FileStandard;
+    UNICODE_STRING FileName;
+
+    RtlZeroMemory(mapping, sizeof(*mapping));
+
+    if(!RtlDosPathNameToNtPathName_U(path, &FileName, NULL, NULL))
+    {
+        RtlFreeUnicodeString(&FileName);
+        return FALSE;
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
+    Status = NtOpenFile(&mapping->file, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
+    RtlFreeUnicodeString(&FileName);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SHIM_ERR("Failed to open file %S: 0x%lx\n", path, Status);
+        return FALSE;
+    }
+
+    Status = NtCreateSection(&mapping->section, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ, 0, 0, PAGE_READONLY, SEC_COMMIT, mapping->file);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Special case */
+        if (Status == STATUS_MAPPED_FILE_SIZE_ZERO)
+        {
+            NtClose(mapping->file);
+            mapping->file = mapping->section = NULL;
+            return TRUE;
+        }
+        SHIM_ERR("Failed to create mapping for file: 0x%lx\n", Status);
+        goto err_out;
+    }
+
+    Status = NtQueryInformationFile(mapping->file, &IoStatusBlock, &FileStandard, sizeof(FileStandard), FileStandardInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        SHIM_ERR("Failed to read file info for file: 0x%lx\n", Status);
+        goto err_out;
+    }
+
+    mapping->mapped_size = mapping->size = FileStandard.EndOfFile.LowPart;
+    Status = NtMapViewOfSection(mapping->section, NtCurrentProcess(), (PVOID*)&mapping->view, 0, 0, 0, &mapping->mapped_size, ViewUnmap, 0, PAGE_READONLY);
+    if (!NT_SUCCESS(Status))
+    {
+        SHIM_ERR("Failed to map view of file: 0x%lx\n", Status);
+        goto err_out;
+    }
+
+    return TRUE;
+
+err_out:
+    if (!mapping->view)
+    {
+        if (mapping->section)
+            NtClose(mapping->section);
+        NtClose(mapping->file);
+    }
+    return FALSE;
+}
+
+void WINAPI SdbpCloseMemMappedFile(PMEMMAPPED mapping)
+{
+    NtUnmapViewOfSection(NtCurrentProcess(), mapping->view);
+    NtClose(mapping->section);
+    NtClose(mapping->file);
+    RtlZeroMemory(mapping, sizeof(*mapping));
+}
+
 /**
  * Converts specified tag into a string.
  *
diff --git a/reactos/dll/appcompat/apphelp/sdbfileattr.c b/reactos/dll/appcompat/apphelp/sdbfileattr.c
new file mode 100644 (file)
index 0000000..143fe16
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2011 André Hentschel
+ * Copyright 2013 Mislav Bla\9eevic
+ * Copyright 2015 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
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "apphelp.h"
+#include "imagehlp.h"
+#include "winver.h"
+
+#include "wine/unicode.h"
+
+#define NUM_ATTRIBUTES  28
+
+static void WINAPI SdbpSetDWORDAttr(PATTRINFO attr, TAG tag, DWORD value)
+{
+    attr->type = tag;
+    attr->flags = ATTRIBUTE_AVAILABLE;
+    attr->dwattr = value;
+}
+
+static void WINAPI SdbpSetQWORDAttr(PATTRINFO attr, TAG tag, QWORD value)
+{
+    attr->type = tag;
+    attr->flags = ATTRIBUTE_AVAILABLE;
+    attr->qwattr = value;
+}
+
+static void WINAPI SdbpSetStringAttr(PATTRINFO attr, TAG tag, WCHAR *string)
+{
+    if (!string)
+    {
+        attr->flags = ATTRIBUTE_FAILED;
+        return;
+    }
+
+    attr->type = tag;
+    attr->flags = ATTRIBUTE_AVAILABLE;
+    attr->lpattr = SdbpStrDup(string);
+}
+
+static void WINAPI SdbpSetAttrFail(PATTRINFO attr)
+{
+    attr->flags = ATTRIBUTE_FAILED;
+}
+
+static WCHAR* WINAPI SdbpGetStringAttr(LPWSTR translation, LPCWSTR attr, PVOID file_info)
+{
+    UINT size = 0;
+    PVOID buffer;
+    WCHAR value[128] = {0};
+
+    if (!file_info)
+        return NULL;
+
+    snprintfW(value, 128, translation, attr);
+    if (VerQueryValueW(file_info, value, &buffer, &size) && size != 0)
+        return (WCHAR*)buffer;
+
+    return NULL;
+}
+
+static void WINAPI SdbpSetStringAttrFromAnsiString(PATTRINFO attr, TAG tag, PBYTE string, BYTE len)
+{
+    WCHAR* dest;
+    if (!string)
+    {
+        attr->flags = ATTRIBUTE_FAILED;
+        return;
+    }
+
+    attr->type = tag;
+    attr->flags = ATTRIBUTE_AVAILABLE;
+    dest = attr->lpattr = SdbpAlloc((len+1) * sizeof(WCHAR));
+    while (len--)
+        *(dest++) = *(string++);
+    *dest = 0;
+}
+
+static void WINAPI SdbpSetStringAttrFromPascalString(PATTRINFO attr, TAG tag, PBYTE string)
+{
+    if (!string)
+    {
+        attr->flags = ATTRIBUTE_FAILED;
+        return;
+    }
+
+    SdbpSetStringAttrFromAnsiString(attr, tag, string + 1, *string);
+}
+
+static void SdbpReadFileVersion(PATTRINFO attr_info, PVOID file_info)
+{
+    static const WCHAR str_root[] = {'\\',0};
+
+    VS_FIXEDFILEINFO* fixed_info;
+    UINT size;
+    if (file_info && VerQueryValueW(file_info, str_root, (LPVOID*)&fixed_info, &size) && size)
+    {
+        if (fixed_info->dwSignature == VS_FFI_SIGNATURE)
+        {
+            LARGE_INTEGER version;
+            version.HighPart = fixed_info->dwFileVersionMS;
+            version.LowPart = fixed_info->dwFileVersionLS;
+            SdbpSetQWORDAttr(&attr_info[2], TAG_BIN_FILE_VERSION, version.QuadPart);
+            SdbpSetQWORDAttr(&attr_info[21], TAG_UPTO_BIN_FILE_VERSION, version.QuadPart);
+            version.HighPart = fixed_info->dwProductVersionMS;
+            version.LowPart = fixed_info->dwProductVersionLS;
+            SdbpSetQWORDAttr(&attr_info[3], TAG_BIN_PRODUCT_VERSION, version.QuadPart);
+            SdbpSetQWORDAttr(&attr_info[22], TAG_UPTO_BIN_PRODUCT_VERSION, version.QuadPart);
+
+            SdbpSetDWORDAttr(&attr_info[12], TAG_VERDATEHI, fixed_info->dwFileDateMS);
+            SdbpSetDWORDAttr(&attr_info[13], TAG_VERDATELO, fixed_info->dwFileDateLS);
+            SdbpSetDWORDAttr(&attr_info[14], TAG_VERFILEOS, fixed_info->dwFileOS);  /* 0x000, 0x4, 0x40004, 0x40000, 0x10004, 0x10001*/
+            SdbpSetDWORDAttr(&attr_info[15], TAG_VERFILETYPE, fixed_info->dwFileType);  /* VFT_APP, VFT_DLL, .... */
+            return;
+        }
+    }
+
+    SdbpSetAttrFail(&attr_info[2]);
+    SdbpSetAttrFail(&attr_info[3]);
+    SdbpSetAttrFail(&attr_info[12]);
+    SdbpSetAttrFail(&attr_info[13]);
+    SdbpSetAttrFail(&attr_info[14]);
+    SdbpSetAttrFail(&attr_info[15]);
+    SdbpSetAttrFail(&attr_info[21]);
+    SdbpSetAttrFail(&attr_info[22]);
+}
+
+static DWORD WINAPI SdbpCalculateFileChecksum(PMEMMAPPED mapping)
+{
+    size_t n, size;
+    PDWORD data;
+    DWORD checks = 0, carry = 0;
+
+    if (mapping->size < 4)
+        return 0;
+
+    if (mapping->size >= 0x1000)
+    {
+        size = 0x1000;
+        if (mapping->size < 0x1200)
+            data = (PDWORD)(mapping->view + mapping->size - size);
+        else
+            data = (PDWORD)mapping->view + (0x200 / 4);
+    }
+    else
+    {
+        data = (PDWORD)mapping->view;
+        size = mapping->size;
+    }
+
+    for (n = 0; n < size / 4; ++n)
+    {
+        checks += *data;
+        carry = (checks & 1) ? 0x80000000 : 0;
+        checks >>= 1;
+        checks |= carry;
+        ++data;
+    }
+    return checks;
+}
+
+static DWORD WINAPI SdbpGetModuleType(PMEMMAPPED mapping)
+{
+    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapping->view;
+    PIMAGE_OS2_HEADER os2;
+
+    if (mapping->size < 2 || dos->e_magic != IMAGE_DOS_SIGNATURE)
+        return 0;
+
+    if (mapping->size < sizeof(IMAGE_DOS_HEADER) || mapping->size < (dos->e_lfanew+2))
+        return 1;
+
+    os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew);
+    if (os2->ne_magic == IMAGE_OS2_SIGNATURE || os2->ne_magic == IMAGE_OS2_SIGNATURE_LE)
+        return 2;
+
+    if (mapping->size >= (dos->e_lfanew + 4) && ((PIMAGE_NT_HEADERS)os2)->Signature == IMAGE_NT_SIGNATURE)
+        return 3;
+
+    return 1;
+}
+
+/**
+ * Frees attribute data allocated by SdbGetFileAttributes.
+ * 
+ * @note Unlike Windows, this implementation will not crash if attr_info is NULL.
+ *
+ * @param [in]  attr_info   Pointer to array of ATTRINFO which will be freed.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info)
+{
+    WORD i;
+
+    if (!attr_info)
+        return FALSE;
+
+    for (i = 0; i < NUM_ATTRIBUTES; i++)
+        if ((attr_info[i].type & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
+            SdbFree(attr_info[i].lpattr);
+    SdbFree(attr_info);
+    return TRUE;
+}
+
+/**
+ * Retrieves attribute data shim database requires to match a file with database entry
+ * 
+ * @note You must free the attr_info allocated by this function by calling SdbFreeFileAttributes.
+ *
+ * @param [in]  path            Path to the file.
+ * @param [out] attr_info_ret   Pointer to array of ATTRINFO. Contains attribute data.
+ * @param [out] attr_count      Number of attributes in attr_info.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbGetFileAttributes(LPCWSTR path, PATTRINFO *attr_info_ret, LPDWORD attr_count)
+{
+    static const WCHAR str_tinfo[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0};
+    static const WCHAR str_trans[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\','%','0','4','x','%','0','4','x','\\','%','%','s',0};
+    static const WCHAR str_CompanyName[] = {'C','o','m','p','a','n','y','N','a','m','e',0};
+    static const WCHAR str_FileDescription[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0};
+    static const WCHAR str_FileVersion[] = {'F','i','l','e','V','e','r','s','i','o','n',0};
+    static const WCHAR str_InternalName[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0};
+    static const WCHAR str_LegalCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0};
+    static const WCHAR str_OriginalFilename[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0};
+    static const WCHAR str_ProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
+    static const WCHAR str_ProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+
+    PIMAGE_NT_HEADERS headers;
+    MEMMAPPED mapped;
+    PVOID file_info = 0;
+    DWORD headersum, checksum, module_type;
+    WCHAR translation[128] = {0};
+    PATTRINFO attr_info;
+
+    struct LANGANDCODEPAGE {
+        WORD language;
+        WORD code_page;
+    } *lang_page;
+
+    if (!SdbpOpenMemMappedFile(path, &mapped))
+    {
+        SHIM_ERR("Error retrieving FILEINFO structure\n");
+        return FALSE;
+    }
+
+    attr_info = (PATTRINFO)SdbAlloc(NUM_ATTRIBUTES * sizeof(ATTRINFO));
+
+    SdbpSetDWORDAttr(&attr_info[0], TAG_SIZE, mapped.size);
+    if (mapped.size)
+        SdbpSetDWORDAttr(&attr_info[1], TAG_CHECKSUM, SdbpCalculateFileChecksum(&mapped));
+    else
+        SdbpSetAttrFail(&attr_info[1]);
+    module_type = SdbpGetModuleType(&mapped);
+
+    if (module_type)
+        SdbpSetDWORDAttr(&attr_info[16], TAG_MODULE_TYPE, module_type);
+    else
+        SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE */
+
+    headers = CheckSumMappedFile(mapped.view, mapped.size, &headersum, &checksum);
+    if (headers)
+    {
+        DWORD info_size;
+        SIZE_T export_dir_size;
+        PIMAGE_EXPORT_DIRECTORY export_dir;
+
+        info_size = GetFileVersionInfoSizeW(path, NULL);
+        if (info_size != 0)
+        {
+            UINT page_size = 0;
+            file_info = SdbAlloc(info_size);
+            GetFileVersionInfoW(path, 0, info_size, file_info);
+            VerQueryValueW(file_info, str_tinfo, (LPVOID)&lang_page, &page_size);
+            snprintfW(translation, 128, str_trans, lang_page->language, lang_page->code_page);
+        }
+
+        /* Handles 2, 3, 12, 13, 14, 15, 21, 22 */
+        SdbpReadFileVersion(attr_info, file_info);
+
+        SdbpSetStringAttr(&attr_info[4], TAG_PRODUCT_VERSION, SdbpGetStringAttr(translation, str_ProductVersion, file_info));
+        SdbpSetStringAttr(&attr_info[5], TAG_FILE_DESCRIPTION, SdbpGetStringAttr(translation, str_FileDescription, file_info));
+        SdbpSetStringAttr(&attr_info[6], TAG_COMPANY_NAME, SdbpGetStringAttr(translation, str_CompanyName, file_info));
+        SdbpSetStringAttr(&attr_info[7], TAG_PRODUCT_NAME, SdbpGetStringAttr(translation, str_ProductName, file_info));
+        SdbpSetStringAttr(&attr_info[8], TAG_FILE_VERSION, SdbpGetStringAttr(translation, str_FileVersion, file_info));
+        SdbpSetStringAttr(&attr_info[9], TAG_ORIGINAL_FILENAME, SdbpGetStringAttr(translation, str_OriginalFilename, file_info));
+        SdbpSetStringAttr(&attr_info[10], TAG_INTERNAL_NAME, SdbpGetStringAttr(translation, str_InternalName, file_info));
+        SdbpSetStringAttr(&attr_info[11], TAG_LEGAL_COPYRIGHT, SdbpGetStringAttr(translation, str_LegalCopyright, file_info));
+
+        /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx */
+
+        SdbpSetDWORDAttr(&attr_info[17], TAG_PE_CHECKSUM, headers->OptionalHeader.CheckSum);
+
+        SdbpSetDWORDAttr(&attr_info[18], TAG_LINKER_VERSION,     /* mislabeled! */
+            ((DWORD)headers->OptionalHeader.MajorImageVersion) << 16 | headers->OptionalHeader.MinorImageVersion);
+        SdbpSetAttrFail(&attr_info[19]); /* TAG_16BIT_DESCRIPTION */
+        SdbpSetAttrFail(&attr_info[20]); /* TAG_16BIT_MODULE_NAME */
+
+        SdbpSetDWORDAttr(&attr_info[23], TAG_LINK_DATE, headers->FileHeader.TimeDateStamp);
+        SdbpSetDWORDAttr(&attr_info[24], TAG_UPTO_LINK_DATE, headers->FileHeader.TimeDateStamp);
+
+        export_dir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(mapped.view, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &export_dir_size);
+        if (export_dir)
+        {
+            PIMAGE_SECTION_HEADER section = NULL;
+            PBYTE export_name = ImageRvaToVa(headers, mapped.view, export_dir->Name, &section);
+            if (export_name)
+                SdbpSetStringAttrFromAnsiString(&attr_info[25], TAG_EXPORT_NAME, export_name, strlen((char*)export_name));
+            else
+                SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
+        }
+        else
+        {
+            SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
+        }
+
+        if (info_size)
+            SdbpSetDWORDAttr(&attr_info[26], TAG_VER_LANGUAGE, lang_page->language);
+
+        SdbpSetDWORDAttr(&attr_info[27], TAG_EXE_WRAPPER, 0); /* boolean */
+    }
+    else
+    {
+        int n;
+        for (n = 2; n < NUM_ATTRIBUTES; ++n)
+        {
+            if (n != 16 && n != 26)
+                SdbpSetAttrFail(&attr_info[n]);
+        }
+        if (module_type == 2)
+        {
+            PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapped.view;
+            PBYTE end = mapped.view + mapped.size, ptr;
+            PIMAGE_OS2_HEADER os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew);
+            if ((PBYTE)(os2 + 1) <= end)
+            {
+                ptr = (PBYTE)dos + os2->ne_nrestab;
+                if (ptr <= end && (ptr + 1 + *ptr) <= end)
+                    SdbpSetStringAttrFromPascalString(&attr_info[19], TAG_16BIT_DESCRIPTION, ptr);
+                ptr = (PBYTE)os2 + os2->ne_restab;
+                if (ptr <= end && (ptr + 1 + *ptr) <= end)
+                    SdbpSetStringAttrFromPascalString(&attr_info[20], TAG_16BIT_MODULE_NAME, ptr);
+            }
+        }
+    }
+
+    *attr_info_ret = attr_info;
+    *attr_count = NUM_ATTRIBUTES; /* As far as I know, this one is always 28 */
+
+    SdbFree(file_info);
+    SdbpCloseMemMappedFile(&mapped);
+    return TRUE;
+}
index be21b2b..cd1376d 100644 (file)
@@ -2,8 +2,9 @@
 add_definitions(-D__ROS_LONG64__)
 
 list(APPEND SOURCE
-    layerapi.c
     apphelp.c
+    data.c
+    layerapi.c
     testlist.c)
 
 add_executable(apphelp_apitest ${SOURCE})
index 8900c7a..f065ba8 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);
+
+#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_Version;
 
@@ -48,6 +57,9 @@ static DWORD g_Version;
 
 
 typedef WORD TAG;
+typedef DWORD TAGID;
+typedef DWORD TAGREF;
+typedef UINT64 QWORD;
 
 #define TAG_TYPE_MASK 0xF000
 
@@ -60,10 +72,55 @@ typedef WORD TAG;
 #define TAG_TYPE_LIST 0x7000
 #define TAG_TYPE_STRING 0x8000
 #define TAG_TYPE_BINARY 0x9000
+#define TAG_NULL 0x0
+#define TAG_SIZE (0x1 | TAG_TYPE_DWORD)
+#define TAG_CHECKSUM (0x3 | TAG_TYPE_DWORD)
+#define TAG_MODULE_TYPE (0x6 | TAG_TYPE_DWORD)
+#define TAG_VERDATEHI (0x7 | TAG_TYPE_DWORD)
+#define TAG_VERDATELO (0x8 | TAG_TYPE_DWORD)
+#define TAG_VERFILEOS (0x9 | TAG_TYPE_DWORD)
+#define TAG_VERFILETYPE (0xA | TAG_TYPE_DWORD)
+#define TAG_PE_CHECKSUM (0xB | TAG_TYPE_DWORD)
+#define TAG_VER_LANGUAGE (0x12 | TAG_TYPE_DWORD)
+#define TAG_LINKER_VERSION (0x1C | TAG_TYPE_DWORD)
+#define TAG_LINK_DATE (0x1D | TAG_TYPE_DWORD)
+#define TAG_UPTO_LINK_DATE (0x1E | TAG_TYPE_DWORD)
+#define TAG_EXE_WRAPPER (0x31 | TAG_TYPE_DWORD)
+#define TAG_BIN_FILE_VERSION (0x2 | TAG_TYPE_QWORD)
+#define TAG_BIN_PRODUCT_VERSION (0x3 | TAG_TYPE_QWORD)
+#define TAG_UPTO_BIN_PRODUCT_VERSION (0x6 | TAG_TYPE_QWORD)
+#define TAG_UPTO_BIN_FILE_VERSION (0xD | TAG_TYPE_QWORD)
+#define TAG_NAME (0x1 | TAG_TYPE_STRINGREF)
+#define TAG_COMPANY_NAME (0x9 | TAG_TYPE_STRINGREF)
+#define TAG_PRODUCT_NAME (0x10 | TAG_TYPE_STRINGREF)
+#define TAG_PRODUCT_VERSION (0x11 | TAG_TYPE_STRINGREF)
+#define TAG_FILE_DESCRIPTION (0x12 | TAG_TYPE_STRINGREF)
+#define TAG_FILE_VERSION (0x13 | TAG_TYPE_STRINGREF)
+#define TAG_ORIGINAL_FILENAME (0x14 | TAG_TYPE_STRINGREF)
+#define TAG_INTERNAL_NAME (0x15 | TAG_TYPE_STRINGREF)
+#define TAG_LEGAL_COPYRIGHT (0x16 | TAG_TYPE_STRINGREF)
+#define TAG_16BIT_DESCRIPTION (0x17 | TAG_TYPE_STRINGREF)
+#define TAG_16BIT_MODULE_NAME (0x20 | TAG_TYPE_STRINGREF)
+#define TAG_EXPORT_NAME (0x24 | TAG_TYPE_STRINGREF)
+
 
+#define ATTRIBUTE_AVAILABLE 0x1
+#define ATTRIBUTE_FAILED 0x2
+
+typedef struct tagATTRINFO {
+  TAG   type;
+  DWORD flags;  /* ATTRIBUTE_AVAILABLE, ATTRIBUTE_FAILED */
+  union {
+    QWORD qwattr;
+    DWORD dwattr;
+    WCHAR *lpattr;
+  };
+} ATTRINFO, *PATTRINFO;
 
 static HMODULE hdll;
 static LPCWSTR (WINAPI *pSdbTagToString)(TAG);
+static BOOL (WINAPI *pSdbGetFileAttributes)(LPCWSTR, PATTRINFO *, LPDWORD);
+static BOOL (WINAPI *pSdbFreeFileAttributes)(PATTRINFO);
 
 static void test_SdbTagToString(void)
 {
@@ -368,6 +425,386 @@ static void test_SdbTagToStringAllTags(void)
     }
 }
 
+static void expect_tag_skip_imp(PATTRINFO pattr, DWORD num)
+{
+    PATTRINFO p = &pattr[num];
+    winetest_ok(p->type == TAG_NULL, "expected entry #%d to be TAG_NULL, was %x\n", num, p->type);
+    winetest_ok(p->flags == ATTRIBUTE_FAILED, "expected entry #%d to be failed, was %d\n", num, p->flags);
+    winetest_ok(p->qwattr == 0, "expected entry #%d to be 0, was 0x%I64x\n", num, p->qwattr);
+}
+static void expect_tag_empty_imp(PATTRINFO pattr, DWORD num)
+{
+    PATTRINFO p = &pattr[num];
+    winetest_ok(p->type == TAG_NULL, "expected entry #%d to be TAG_NULL, was %x\n", num, p->type);
+    winetest_ok(p->flags == 0, "expected entry #%d to be 0, was %d\n", num, p->flags);
+    winetest_ok(p->qwattr == 0, "expected entry #%d to be 0, was 0x%I64x\n", num, p->qwattr);
+}
+
+static void expect_tag_dword_imp(PATTRINFO pattr, DWORD num, TAG tag, DWORD value)
+{
+    PATTRINFO p = &pattr[num];
+    winetest_ok(p->type == tag, "expected entry #%d to be %x, was %x\n", num, tag, p->type);
+    winetest_ok(p->flags == ATTRIBUTE_AVAILABLE, "expected entry #%d to be available, was %d\n", num, p->flags);
+    winetest_ok(p->dwattr == value, "expected entry #%d to be 0x%x, was 0x%x\n", num, value, p->dwattr);
+}
+
+static void expect_tag_qword_imp(PATTRINFO pattr, DWORD num, TAG tag, QWORD value)
+{
+    PATTRINFO p = &pattr[num];
+    winetest_ok(p->type == tag, "expected entry #%d to be %x, was %x\n", num, tag, p->type);
+    winetest_ok(p->flags == ATTRIBUTE_AVAILABLE, "expected entry #%d to be available, was %d\n", num, p->flags);
+    winetest_ok(p->qwattr == value, "expected entry #%d to be 0x%I64x, was 0x%I64x\n", num, value, p->qwattr);
+}
+
+static void expect_tag_str_imp(PATTRINFO pattr, DWORD num, TAG tag, const WCHAR* value)
+{
+    PATTRINFO p = &pattr[num];
+    winetest_ok(p->type == tag, "expected entry #%d to be %x, was %x\n", num, tag, p->type);
+    winetest_ok(p->flags == ATTRIBUTE_AVAILABLE, "expected entry #%d to be available, was %d\n", num, p->flags);
+    winetest_ok(p->lpattr && wcscmp(p->lpattr, value) == 0, "expected entry #%d to be %s, was %s\n", num, wine_dbgstr_w(value), wine_dbgstr_w(p->lpattr));
+}
+
+#define expect_tag_skip     (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : expect_tag_skip_imp
+#define expect_tag_empty    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : expect_tag_empty_imp
+#define expect_tag_dword    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : expect_tag_dword_imp
+#define expect_tag_qword    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : expect_tag_qword_imp
+#define expect_tag_str      (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : expect_tag_str_imp
+#define expect_tag_skip_range(ptr, from, to) \
+    do { \
+        int n = (from), n_end = (to); \
+        winetest_set_location(__FILE__, __LINE__); \
+        for ( ; n < n_end; ++n) \
+            expect_tag_skip_imp((ptr), n); \
+    } while (0)
+#define test_crc            (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_crc_imp
+#define test_crc2           (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_crc2_imp
+
+void test_onefile(WCHAR* filename)
+{
+    PATTRINFO pattrinfo;
+    DWORD num;
+
+    if (!pSdbFreeFileAttributes)
+    {
+        hdll = LoadLibraryA("apphelp.dll");
+        pSdbTagToString = (void *)GetProcAddress(hdll, "SdbTagToString");
+        pSdbGetFileAttributes = (void *)GetProcAddress(hdll, "SdbGetFileAttributes");
+        pSdbFreeFileAttributes = (void *)GetProcAddress(hdll, "SdbFreeFileAttributes");
+    }
+
+    if (pSdbGetFileAttributes(filename, &pattrinfo, &num))
+    {
+        if (pattrinfo[16].flags == ATTRIBUTE_AVAILABLE)
+        {
+            if (pattrinfo[16].type != TAG_MODULE_TYPE)//SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE (1: WIN16?) (3: WIN32?) (WIN64?), Win32VersionValue? */)
+                printf("FAIL TAG_MODULE_TYPE (%S)\n", filename);
+            if (pattrinfo[16].dwattr != 3 && pattrinfo[16].dwattr != 2)
+                printf("TAG_MODULE_TYPE(%S): %d\n", filename, pattrinfo[16].dwattr);    // C:\Program Files (x86)\Windows Kits\8.1\Lib\win7\stub512.com
+            if (pattrinfo[16].dwattr == 2)
+            {
+                printf("TAG_MODULE_TYPE(%S): %d, %d\n", filename, pattrinfo[16].dwattr, pattrinfo[0].dwattr);
+            }
+        }
+
+        if (pattrinfo[27].flags == ATTRIBUTE_AVAILABLE)
+        {
+            if (pattrinfo[27].type != TAG_EXE_WRAPPER)
+                printf("FAIL TAG_EXE_WRAPPER (%S)\n", filename);
+            if (pattrinfo[27].dwattr != 0)
+                printf("TAG_EXE_WRAPPER(%S): %d\n", filename, pattrinfo[27].dwattr);
+        }
+
+        pSdbFreeFileAttributes(pattrinfo);
+    }
+}
+
+static void test_crc_imp(size_t len, DWORD expected)
+{
+    static const WCHAR path[] = {'t','e','s','t','x','x','.','e','x','e',0};
+    static char crc_test[] = {4, 4, 4, 4, 1, 1, 1, 1, 4, 4, 4, 4, 2, 2, 2, 2};
+
+    PATTRINFO pattrinfo = (PATTRINFO)0xdead;
+    DWORD num = 333;
+    BOOL ret;
+
+    test_create_file_imp("testxx.exe", crc_test, len);
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    winetest_ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    winetest_ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    winetest_ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword_imp(pattrinfo, 1, TAG_CHECKSUM, expected);
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+}
+
+static void test_crc2_imp(size_t len, int fill, DWORD expected)
+{
+    static const WCHAR path[] = {'t','e','s','t','x','x','.','e','x','e',0};
+
+    PATTRINFO pattrinfo = (PATTRINFO)0xdead;
+    DWORD num = 333;
+    BOOL ret;
+    size_t n;
+    char* crc_test = malloc(len);
+    for (n = 0; n < len; ++n)
+        crc_test[n] = (char)(fill ? fill : n);
+
+    test_create_file_imp("testxx.exe", crc_test, len);
+    free(crc_test);
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    winetest_ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    winetest_ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    winetest_ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword_imp(pattrinfo, 0, TAG_SIZE, len);
+        expect_tag_dword_imp(pattrinfo, 1, TAG_CHECKSUM, expected);
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+}
+
+
+
+static void test_ApplicationAttributes(void)
+{
+    static const WCHAR path[] = {'t','e','s','t','x','x','.','e','x','e',0};
+    static const WCHAR PRODUCT_VERSION[] = {'1','.','0','.','0','.','1',0};
+    static const WCHAR FILE_DESCRIPTION[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0};
+    static const WCHAR COMPANY_NAME[] = {'C','o','m','p','a','n','y','N','a','m','e',0};
+    static const WCHAR PRODUCT_NAME[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
+    static const WCHAR FILE_VERSION[] = {'1','.','0','.','0','.','0',0};
+    static const WCHAR ORIGINAL_FILENAME[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0};
+    static const WCHAR INTERNAL_NAME[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0};
+    static const WCHAR LEGAL_COPYRIGHT[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0};
+    static const WCHAR EXPORT_NAME[] = {'T','e','S','t','2','.','e','x','e',0};
+    static const WCHAR OS2_DESCRIPTION[] = {'M','O','D',' ','D','E','S','C','R','I','P','T','I','O','N',' ','H','E','R','E',0};
+    static const WCHAR OS2_EXPORT_NAME[] = {'T','E','S','T','M','O','D','.','h','X','x',0};
+    static const WCHAR OS2_DESCRIPTION_broken[] = {'Z',0};
+    static const WCHAR OS2_EXPORT_NAME_broken[] = {'E',0};
+
+    PATTRINFO pattrinfo = (PATTRINFO)0xdead;
+    DWORD num = 333;
+    BOOL ret;
+
+    /* ensure the file is not there. */
+    DeleteFileA("testxx.exe");
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret == FALSE, "expected SdbGetFileAttributes to fail.\n");
+    ok(pattrinfo == (PATTRINFO)0xdead, "expected the pointer not to change.\n");
+    ok(num == 333, "expected the number of items not to change.\n");
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    /* Test a file with as much features as possible */
+    test_create_exe("testxx.exe", 0);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0x800);
+        expect_tag_dword(pattrinfo, 1, TAG_CHECKSUM, 0x178bd629);
+        expect_tag_qword(pattrinfo, 2, TAG_BIN_FILE_VERSION, 0x1000000000000ull);
+        expect_tag_qword(pattrinfo, 3, TAG_BIN_PRODUCT_VERSION, 0x1000000000001ull);
+        expect_tag_str(pattrinfo, 4, TAG_PRODUCT_VERSION, PRODUCT_VERSION);
+        expect_tag_str(pattrinfo, 5, TAG_FILE_DESCRIPTION, FILE_DESCRIPTION);
+        expect_tag_str(pattrinfo, 6, TAG_COMPANY_NAME, COMPANY_NAME);
+        expect_tag_str(pattrinfo, 7, TAG_PRODUCT_NAME, PRODUCT_NAME);
+        expect_tag_str(pattrinfo, 8, TAG_FILE_VERSION, FILE_VERSION);
+        expect_tag_str(pattrinfo, 9, TAG_ORIGINAL_FILENAME, ORIGINAL_FILENAME);
+        expect_tag_str(pattrinfo, 10, TAG_INTERNAL_NAME, INTERNAL_NAME);
+        expect_tag_str(pattrinfo, 11, TAG_LEGAL_COPYRIGHT, LEGAL_COPYRIGHT);
+        expect_tag_dword(pattrinfo, 12, TAG_VERDATEHI, 0x1d1a019);
+        expect_tag_dword(pattrinfo, 13, TAG_VERDATELO, 0xac754c50);
+        expect_tag_dword(pattrinfo, 14, TAG_VERFILEOS, VOS__WINDOWS32);
+        expect_tag_dword(pattrinfo, 15, TAG_VERFILETYPE, VFT_APP);
+        expect_tag_dword(pattrinfo, 16, TAG_MODULE_TYPE, 0x3); /* Win32 */
+        expect_tag_dword(pattrinfo, 17, TAG_PE_CHECKSUM, 0xBAAD);
+        expect_tag_dword(pattrinfo, 18, TAG_LINKER_VERSION, 0x40002);
+        expect_tag_skip(pattrinfo, 19); /* TAG_16BIT_DESCRIPTION */
+        expect_tag_skip(pattrinfo, 20); /* TAG_16BIT_MODULE_NAME */
+        expect_tag_qword(pattrinfo, 21, TAG_UPTO_BIN_FILE_VERSION, 0x1000000000000ull);
+        expect_tag_qword(pattrinfo, 22, TAG_UPTO_BIN_PRODUCT_VERSION, 0x1000000000001ull);
+        expect_tag_dword(pattrinfo, 23, TAG_LINK_DATE, 0x12345);
+        expect_tag_dword(pattrinfo, 24, TAG_UPTO_LINK_DATE, 0x12345);
+        expect_tag_str(pattrinfo, 25, TAG_EXPORT_NAME, EXPORT_NAME);
+        expect_tag_dword(pattrinfo, 26, TAG_VER_LANGUAGE, 0xffff);
+        expect_tag_dword(pattrinfo, 27, TAG_EXE_WRAPPER, 0x0);
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+
+    /* Disable resource and exports */
+    test_create_exe("testxx.exe", 1);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0x800);
+        expect_tag_dword(pattrinfo, 1, TAG_CHECKSUM, 0xea7caffd);
+        expect_tag_skip_range(pattrinfo, 2, 16);
+        expect_tag_dword(pattrinfo, 16, TAG_MODULE_TYPE, 0x3); /* Win32 */
+        expect_tag_dword(pattrinfo, 17, TAG_PE_CHECKSUM, 0xBAAD);
+        expect_tag_dword(pattrinfo, 18, TAG_LINKER_VERSION, 0x40002);
+        expect_tag_skip_range(pattrinfo, 19, 23);
+        expect_tag_dword(pattrinfo, 23, TAG_LINK_DATE, 0x12345);
+        expect_tag_dword(pattrinfo, 24, TAG_UPTO_LINK_DATE, 0x12345);
+        expect_tag_skip(pattrinfo, 25); /* TAG_EXPORT_NAME */
+        expect_tag_empty(pattrinfo, 26); /* TAG_VER_LANGUAGE */
+        expect_tag_dword(pattrinfo, 27, TAG_EXE_WRAPPER, 0x0);
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    /* A file with just 'MZ' */
+    test_create_file("testxx.exe", "MZ", 2);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0x2);
+        expect_tag_dword(pattrinfo, 1, TAG_CHECKSUM, 0);
+        expect_tag_skip_range(pattrinfo, 2, 16);
+        expect_tag_dword(pattrinfo, 16, TAG_MODULE_TYPE, 0x1);
+        expect_tag_skip_range(pattrinfo, 17, 26);
+        expect_tag_empty(pattrinfo, 26); /* TAG_VER_LANGUAGE */
+        expect_tag_skip(pattrinfo, 27); /* TAG_EXE_WRAPPER */
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    /* Empty file */
+    test_create_file("testxx.exe", NULL, 0);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0);
+        expect_tag_skip_range(pattrinfo, 1, 26);
+        expect_tag_empty(pattrinfo, 26); /* TAG_VER_LANGUAGE */
+        expect_tag_skip(pattrinfo, 27); /* TAG_EXE_WRAPPER */
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    /* minimal NE executable */
+    test_create_ne("testxx.exe", 0);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0xa8);
+        expect_tag_dword(pattrinfo, 1, TAG_CHECKSUM, 0xf2abe4e9);
+        expect_tag_skip_range(pattrinfo, 2, 16);
+        expect_tag_dword(pattrinfo, 16, TAG_MODULE_TYPE, 0x2);
+        expect_tag_skip(pattrinfo, 17); /* TAG_PE_CHECKSUM */
+        expect_tag_skip(pattrinfo, 18); /* TAG_LINKER_VERSION */
+        expect_tag_str(pattrinfo, 19, TAG_16BIT_DESCRIPTION, OS2_DESCRIPTION);
+        expect_tag_str(pattrinfo, 20, TAG_16BIT_MODULE_NAME, OS2_EXPORT_NAME);
+        expect_tag_skip_range(pattrinfo, 21, 26);
+        expect_tag_empty(pattrinfo, 26); /* TAG_VER_LANGUAGE */
+        expect_tag_skip(pattrinfo, 27); /* TAG_EXE_WRAPPER */
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    /* NE executable with description / module name pointers zero, to show they are always used */
+    test_create_ne("testxx.exe", 1);
+
+    ret = pSdbGetFileAttributes(path, &pattrinfo, &num);
+    ok(ret != FALSE, "expected SdbGetFileAttributes to succeed.\n");
+    ok(pattrinfo != (PATTRINFO)0xdead, "expected a valid pointer.\n");
+    ok(num == 28, "expected 28 items, got %d.\n", num);
+
+    if (num == 28 && ret)
+    {
+        expect_tag_dword(pattrinfo, 0, TAG_SIZE, 0xa8);
+        expect_tag_dword(pattrinfo, 1, TAG_CHECKSUM, 0xddcbe4c9);
+        expect_tag_skip_range(pattrinfo, 2, 16);
+        expect_tag_dword(pattrinfo, 16, TAG_MODULE_TYPE, 0x2);
+        expect_tag_skip(pattrinfo, 17); /* TAG_PE_CHECKSUM */
+        expect_tag_skip(pattrinfo, 18); /* TAG_LINKER_VERSION */
+        expect_tag_str(pattrinfo, 19, TAG_16BIT_DESCRIPTION, OS2_DESCRIPTION_broken);   /* the 'Z' from 'MZ' */
+        expect_tag_str(pattrinfo, 20, TAG_16BIT_MODULE_NAME, OS2_EXPORT_NAME_broken);   /* the 'E' from 'NE' */
+        expect_tag_skip_range(pattrinfo, 21, 26);
+        expect_tag_empty(pattrinfo, 26); /* TAG_VER_LANGUAGE */
+        expect_tag_skip(pattrinfo, 27); /* TAG_EXE_WRAPPER */
+    }
+    if (ret)
+        pSdbFreeFileAttributes(pattrinfo);
+
+    test_crc(1, 0);
+    test_crc(2, 0);
+    test_crc(3, 0);
+    test_crc(4, 0x2020202);
+    test_crc(5, 0x2020202);
+    test_crc(6, 0x2020202);
+    test_crc(7, 0x2020202);
+    test_crc(8, 0x81818181);
+    test_crc(9, 0x81818181);
+    test_crc(10, 0x81818181);
+    test_crc(11, 0x81818181);
+    test_crc(12, 0xc2c2c2c2);
+    test_crc(16, 0x62626262);
+
+    /* This seems to be the cutoff point */
+    test_crc2(0xffc, 4, 0xfbfbfcfc);
+    test_crc2(0xffc, 8, 0x7070717);
+    test_crc2(0xffc, 0xcc, 0xc8eba002);
+    test_crc2(0xffc, 0, 0x4622028d);
+
+    test_crc2(0x1000, 4, 0x80);
+    test_crc2(0x1000, 8, 0x8787878f);
+    test_crc2(0x1000, 0xcc, 0x4adc3667);
+    test_crc2(0x1000, 0, 0xa3108044);
+
+    /* Here is another cutoff point */
+    test_crc2(0x11fc, 4, 0x80);
+    test_crc2(0x11fc, 8, 0x8787878f);
+    test_crc2(0x11fc, 0xcc, 0x4adc3667);
+    test_crc2(0x11fc, 0, 0xf03e0800);
+
+    test_crc2(0x1200, 4, 0x80);
+    test_crc2(0x1200, 8, 0x8787878f);
+    test_crc2(0x1200, 0xcc, 0x4adc3667);
+    test_crc2(0x1200, 0, 0xa3108044);
+
+    /* After that, it stays the same for all sizes */
+    test_crc2(0xf000, 4, 0x80);
+    test_crc2(0xf000, 8, 0x8787878f);
+    test_crc2(0xf000, 0xcc, 0x4adc3667);
+    test_crc2(0xf000, 0, 0xa3108044);
+
+
+    DeleteFileA("testxx.exe");
+}
+
 START_TEST(apphelp)
 {
     RTL_OSVERSIONINFOEXW rtlinfo;
@@ -383,6 +820,10 @@ START_TEST(apphelp)
     //SetEnvironmentVariable("DEBUGCHANNEL", "+apphelp");
     hdll = LoadLibraryA("apphelp.dll");
     pSdbTagToString = (void *) GetProcAddress(hdll, "SdbTagToString");
+    pSdbGetFileAttributes = (void *) GetProcAddress(hdll, "SdbGetFileAttributes");
+    pSdbFreeFileAttributes = (void *) GetProcAddress(hdll, "SdbFreeFileAttributes");
+
+    test_ApplicationAttributes();
     test_SdbTagToString();
 #ifdef __REACTOS__
     if (g_Version < VERSION_WIN7)
diff --git a/rostests/apitests/apphelp/data.c b/rostests/apitests/apphelp/data.c
new file mode 100644 (file)
index 0000000..be732d6
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2015 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
+ */
+
+#include <exdisp.h>
+#include <winbase.h>
+#include "wine/test.h"
+
+
+static IMAGE_DOS_HEADER dos_header =
+{
+    IMAGE_DOS_SIGNATURE, /* e_magic */
+    144, /* e_cblp */
+    3, /* e_cp */
+    0, /* e_crlc */
+    4, /* e_cparhdr */
+    0, /* e_minalloc */
+    65535, /* e_maxalloc */
+    0, /* e_ss */
+    184, /* e_sp */
+    0, /* e_csum */
+    0, /* e_ip */
+    0, /* e_cs */
+    64, /* e_lfarlc */
+    0, /* e_ovno */
+    { 0 }, /* e_res[4] */
+    0, /* e_oemid */
+    0, /* e_oeminfo */
+    { 0 }, /* e_res2[10] */
+    0x80 /* e_lfanew */
+};
+
+static IMAGE_NT_HEADERS32 nt_header =
+{
+    IMAGE_NT_SIGNATURE, /* Signature */
+    {
+        IMAGE_FILE_MACHINE_I386, /* Machine */
+        2, /* NumberOfSections */
+        0x12345, /* TimeDateStamp */
+        0, /* PointerToSymbolTable */
+        0, /* NumberOfSymbols */
+        sizeof(IMAGE_OPTIONAL_HEADER), /* SizeOfOptionalHeader */
+        IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED | IMAGE_FILE_32BIT_MACHINE /* Characteristics */
+    },
+    {
+        IMAGE_NT_OPTIONAL_HDR_MAGIC, /* Magic */
+        6, /* MajorLinkerVersion */
+        3, /* MinorLinkerVersion */
+        0, /* SizeOfCode */
+        0, /* SizeOfInitializedData */
+        0, /* SizeOfUninitializedData */
+        0x1000, /* AddressOfEntryPoint */
+        0x1000, /* BaseOfCode */
+#ifndef _WIN64
+        0, /* BaseOfData */
+#endif
+        0x400000, /* ImageBase */
+        0x1000, /* SectionAlignment */
+        0x200, /* FileAlignment */
+        4, /* MajorOperatingSystemVersion */
+        1, /* MinorOperatingSystemVersion */
+        4, /* MajorImageVersion */
+        2, /* MinorImageVersion */
+        4, /* MajorSubsystemVersion */
+        3, /* MinorSubsystemVersion */
+        0, /* Win32VersionValue */
+        0x3000, /* SizeOfImage */
+        0x200, /* SizeOfHeaders */
+        0xBAAD, /* CheckSum: This checksum is not the correct checksum, intentionally! */
+        IMAGE_SUBSYSTEM_WINDOWS_CUI, /* Subsystem */
+        0, /* DllCharacteristics */
+        0x100000, /* SizeOfStackReserve */
+        0x1000, /* SizeOfStackCommit */
+        0x100000, /* SizeOfHeapReserve */
+        0x1000, /* SizeOfHeapCommit */
+        0, /* LoaderFlags */
+        0x10, /* NumberOfRvaAndSizes */
+        {
+            /* IMAGE_DIRECTORY_ENTRY_EXPORT */
+            {
+                0x2370, /* VirtualAddress */
+                76, /* Size */
+            },
+            { 0 },
+            /* IMAGE_DIRECTORY_ENTRY_RESOURCE */
+            {
+                0x2000, /* VirtualAddress */
+                868, /* Size */
+            },
+        } /* DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] */
+    }
+};
+
+static IMAGE_SECTION_HEADER section_headers[] =
+{
+    {
+        { '.','t','e','x','t',0 }, /* Name */
+        { 24 }, /* VirtualSize */
+        0x1000, /* VirtualAddress */
+        0x200, /* SizeOfRawData */
+        0x200, /* PointerToRawData */
+        0, /* PointerToRelocations */
+        0, /* PointerToLinenumbers */
+        0, /* NumberOfRelocations */
+        0, /* NumberOfLinenumbers */
+        IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ /* Characteristics */
+    },
+    {
+        { '.','r','s','r','c',0 }, /* Name */
+        { 880 }, /* VirtualSize */
+        0x2000, /* VirtualAddress */
+        0x400, /* SizeOfRawData */
+        0x400, /* PointerToRawData */
+        0, /* PointerToRelocations */
+        0, /* PointerToLinenumbers */
+        0, /* NumberOfRelocations */
+        0, /* NumberOfLinenumbers */
+        IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ /* Characteristics */
+    }
+};
+
+static const unsigned char text_section[] =
+{
+    0x33, 0xc0,     /* xor eax, eax */
+    0xc3            /* ret */
+};
+
+
+/* taken from fusionpriv.h */
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+    WCHAR szKey[17];
+    VS_FIXEDFILEINFO Value;
+} VS_VERSIONINFO;
+
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+    WCHAR szKey[15];
+} STRINGFILEINFO;
+
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+    WCHAR szKey[9];
+} STRINGTABLE;
+
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+} STRINGHDR;
+
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+    WCHAR szKey[13];
+} VARFILEINFO;
+
+typedef struct
+{
+    WORD wLength;
+    WORD wValueLength;
+    WORD wType;
+    WCHAR szKey[13];
+    DWORD Value;
+} VAR;
+
+typedef struct rsrc_section_t
+{
+    IMAGE_RESOURCE_DIRECTORY header;
+    IMAGE_RESOURCE_DIRECTORY_ENTRY file_info_id;
+    IMAGE_RESOURCE_DIRECTORY file_info_header;
+    IMAGE_RESOURCE_DIRECTORY_ENTRY version_info_id;
+    IMAGE_RESOURCE_DIRECTORY version_info_header;
+    IMAGE_RESOURCE_DIRECTORY_ENTRY version_lang_id;
+    IMAGE_RESOURCE_DATA_ENTRY version_data_entry;
+    
+    VS_VERSIONINFO version_info;
+    STRINGFILEINFO string_file_info;
+    STRINGTABLE string_table;
+
+    STRINGHDR FileVersion_hdr;
+    WCHAR FileVersion_key[13];
+    WCHAR FileVersion_val[8];
+
+    STRINGHDR ProductVersion_hdr;
+    WCHAR ProductVersion_key[15];
+    WCHAR ProductVersion_val[8];
+
+    STRINGHDR CompanyName_hdr;
+    WCHAR CompanyName_key[13];
+    WCHAR CompanyName_val[12];
+
+    STRINGHDR FileDescription_hdr;
+    WCHAR FileDescription_key[17];
+    WCHAR FileDescription_val[16];
+
+    STRINGHDR InternalName_hdr;
+    WCHAR InternalName_key[13];
+    WCHAR InternalName_val[14];
+
+    STRINGHDR LegalCopyright_hdr;
+    WCHAR LegalCopyright_key[15];
+    WCHAR LegalCopyright_val[16];
+
+    STRINGHDR LegalTrademarks_hdr;
+    WCHAR LegalTrademarks_key[17];
+    WCHAR LegalTrademarks_val[16];
+
+    STRINGHDR OriginalFilename_hdr;
+    WCHAR OriginalFilename_key[17];
+    WCHAR OriginalFilename_val[18];
+
+    STRINGHDR Productname_hdr;
+    WCHAR Productname_key[13];
+    WCHAR Productname_val[12];
+
+    VARFILEINFO file_info;
+    VAR translation;
+} rsrc_section_t;
+
+static const rsrc_section_t rsrc_section =
+{
+    /* header */
+    {
+        0, /* Characteristics */
+        0x55FE8E21, /* TimeDateStamp, 20/09/2015 10:44:49 */
+        0, /* MajorVersion */
+        0, /* MinorVersion */
+        0, /* NumberOfNamedEntries */
+        1, /* NumberOfIdEntries */
+    },
+    /* file_info_id */
+    {
+        {{
+            (DWORD)VS_FILE_INFO, /* NameOffset:31 */
+            0 /* NameIsString:1 */
+        }},
+        {
+            0x80000018 /* OffsetToData */
+        }
+    },
+    /* file_info_header */
+    {
+        0, /* Characteristics */
+        0x55FE8E21, /* TimeDateStamp, 20/09/2015 10:44:49 */
+        0, /* MajorVersion */
+        0, /* MinorVersion */
+        0, /* NumberOfNamedEntries */
+        1, /* NumberOfIdEntries */
+    },
+    /* version_info_id */
+    {
+        {{
+            VS_VERSION_INFO, /* NameOffset:31 */
+            0 /* NameIsString:1 */
+        }},
+        {
+            0x80000030 /* OffsetToData */
+        },
+    },
+    /* version_info_header */
+    {
+        0, /* Characteristics */
+        0x55FE8E21, /* TimeDateStamp, 20/09/2015 10:44:49 */
+        0, /* MajorVersion */
+        0, /* MinorVersion */
+        0, /* NumberOfNamedEntries */
+        1, /* NumberOfIdEntries */
+    },
+    /* version_lang_id */
+    {
+        {{
+            1033, /* NameOffset:31 */
+            0 /* NameIsString:1 */
+        }},
+        {
+            0x48 /* OffsetToDirectory */
+        }
+    },
+    /* version_data_entry */
+    {
+        0x2058, /* OffsetToData */
+        0x30C, /* Size */
+        0, /* CodePage */
+        0, /* Reserved */
+    },
+
+    /* version_info */
+    {
+    0x30C, /* wLength */
+    0x34, /* wValueLength */
+    0, /* wType: Binary */
+    { 'V','S','_','V','E','R','S','I','O','N','_','I','N','F','O','\0','\0' }, /* szKey[17] */
+        /* Value */
+        {
+            0xFEEF04BD, /* dwSignature */
+            0x10000, /* dwStrucVersion */
+            0x10000, /* dwFileVersionMS */
+            0, /* dwFileVersionLS */
+            0x10000, /* dwProductVersionMS */
+            1, /* dwProductVersionLS */
+            0, /* dwFileFlagsMask */
+            0, /* dwFileFlags */
+            VOS__WINDOWS32, /* dwFileOS */
+            VFT_APP, /* dwFileType */
+            0, /* dwFileSubtype */
+            0x01d1a019, /* dwFileDateMS */
+            0xac754c50 /* dwFileDateLS */
+        },
+    },
+
+    /* string_file_info */
+    {
+        0x26C, /* wLength */
+        0, /* wValueLength */
+        1, /* wType: Text */
+        { 'S','t','r','i','n','g','F','i','l','e','I','n','f','o','\0' } /* szKey[15] */
+    },
+    /* string_table */
+    {
+        0x248, /* wLength */
+        0, /* wValueLength */
+        1, /* wType: Text */
+        { 'F','F','F','F','0','0','0','0','\0' } /* szKey[9] */
+    },
+
+    /* FileVersion */
+    {
+        48, /* wLength */
+        8, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'F','i','l','e','V','e','r','s','i','o','n','\0' },
+    { '1','.','0','.','0','.','0','\0' },
+
+    /* ProductVersion */
+    {
+        52, /* wLength */
+        8, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'P','r','o','d','u','c','t','V','e','r','s','i','o','n','\0' },
+    { '1','.','0','.','0','.','1','\0' },
+
+    /* CompanyName */
+    {
+        56, /* wLength */
+        12, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'C','o','m','p','a','n','y','N','a','m','e','\0' },
+    { 'C','o','m','p','a','n','y','N','a','m','e','\0' },
+
+    /* FileDescription */
+    {
+        72, /* wLength */
+        16, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n','\0' },
+    { 'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n','\0' },
+
+    /* InternalName */
+    {
+        58, /* wLength */
+        13, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'I','n','t','e','r','n','a','l','N','a','m','e','\0' },
+    { 'I','n','t','e','r','n','a','l','N','a','m','e','\0' },
+
+    /* LegalCopyright */
+    {
+        66, /* wLength */
+        15, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'L','e','g','a','l','C','o','p','y','r','i','g','h','t','\0' },
+    { 'L','e','g','a','l','C','o','p','y','r','i','g','h','t','\0' },
+
+    /* LegalTrademarks */
+    {
+        72, /* wLength */
+        16, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'L','e','g','a','l','T','r','a','d','e','m','a','r','k','s','\0' },
+    { 'L','e','g','a','l','T','r','a','d','e','m','a','r','k','s','\0' },
+
+    /* OriginalFilename */
+    {
+        74, /* wLength */
+        17, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e','\0' },
+    { 'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e','\0' },
+
+    /* ProductName */
+    {
+        56, /* wLength */
+        12, /* wValueLength */
+        1, /* wType: Text */
+    },
+    { 'P','r','o','d','u','c','t','N','a','m','e','\0' },
+    { 'P','r','o','d','u','c','t','N','a','m','e','\0' },
+
+
+    /* file_info */
+    {
+        0x44, /* wLength */
+        0, /* wValueLength */
+        1, /* wType: Text */
+        { 'V','a','r','F','i','l','e','I','n','f','o','\0' } /* szKey[13] */
+    },
+
+    /* translation */
+    {
+        0x24, /* wLength */
+        4, /* wValueLength */
+        0, /* wType: Binary */
+        { 'T','r','a','n','s','l','a','t','i','o','n','\0' }, /* szKey[13] */
+        0xffff /* Value */
+    }
+};
+
+typedef struct export_section_t
+{
+    IMAGE_EXPORT_DIRECTORY desc;
+    char binary_name[10];
+} export_section_t;
+
+/* This export section is not complete, but the Name RVA is only taken into account */
+static export_section_t export_dir = 
+{
+    {
+        0, /* Characteristics */
+        0, /* TimeDateStamp */
+        0, /* MajorVersion */
+        0, /* MinorVersion */
+        0x2398, /* Name (RVA) */
+        1, /* Base */
+        0, /* NumberOfFunctions */
+        0, /* NumberOfNames */
+        0, /* AddressOfFunctions (RVA)  */
+        0, /* AddressOfNames (RVA) */
+        0, /* AddressOfNameOrdinals (RVA) */
+    },
+    { 'T','e','S','t','2','.','e','x','e',0 }, /* binary_name */
+};
+
+
+void test_create_exe_imp(const char* name, int skip_rsrc_exports)
+{
+    HANDLE file;
+    char *buf, *cur;
+    DWORD size = 0x800;
+    buf = malloc(size);
+
+    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    winetest_ok(file != INVALID_HANDLE_VALUE, "can't create file\n");
+    if(file == INVALID_HANDLE_VALUE)
+        return;
+
+    memset(buf, 0, size);
+    cur = buf;
+    cur = memcpy(buf, &dos_header, sizeof(dos_header));
+    cur += dos_header.e_lfanew;
+
+    memcpy(cur, &nt_header, sizeof(nt_header));
+    if (skip_rsrc_exports)
+    {
+        ((IMAGE_NT_HEADERS32*)cur)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = 0;
+        ((IMAGE_NT_HEADERS32*)cur)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0;
+    }
+    cur += sizeof(nt_header);
+    memcpy(cur, section_headers, sizeof(section_headers));
+
+    /* write code section: */
+    cur = buf + section_headers[0].PointerToRawData;
+    memcpy(cur, text_section, sizeof(text_section));
+
+    if (!skip_rsrc_exports)
+    {
+        /* write resource section: */
+        cur = buf + section_headers[1].PointerToRawData;
+        memcpy(cur, &rsrc_section, sizeof(rsrc_section));
+
+        /* write minimal export directory: */
+        cur += 0x370;
+        memcpy(cur, &export_dir, sizeof(export_dir));
+    }
+
+    WriteFile(file, buf, size, &size, NULL);
+    free(buf);
+    CloseHandle(file);
+}
+
+
+/* Almost everything in this filetype is ignored, only e_lfanew, ne_restab and ne_nrestab are relevant */
+void test_create_ne_imp(const char* name, int skip_names)
+{
+    HANDLE file;
+    DWORD size;
+    IMAGE_DOS_HEADER MZ_hdr = { IMAGE_DOS_SIGNATURE, 0 };
+    IMAGE_OS2_HEADER NE_hdr = { IMAGE_OS2_SIGNATURE, 0 };
+    static const BYTE NE_names[] =
+    {
+        /* Show that the length is used, not the nullterm*/
+        11,'T','E','S','T','M','O','D','.','h','X','x','x',0,0,0,
+        20,'M','O','D',' ','D','E','S','C','R','I','P','T','I','O','N',' ','H','E','R','E',0,0,0
+    };
+
+    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    winetest_ok(file != INVALID_HANDLE_VALUE, "can't create file\n");
+    if(file == INVALID_HANDLE_VALUE)
+        return;
+
+    MZ_hdr.e_lfanew = sizeof(MZ_hdr);
+    if (!skip_names)
+    {
+        NE_hdr.ne_restab = sizeof(NE_hdr);  /* First entry (pascal string + ordinal) = module name */
+        NE_hdr.ne_nrestab = sizeof(MZ_hdr) + sizeof(NE_hdr) + 16;  /* First entry (pascal string + ordinal) = module description */
+    }
+
+    WriteFile(file, &MZ_hdr, sizeof(MZ_hdr), &size, NULL);
+    WriteFile(file, &NE_hdr, sizeof(NE_hdr), &size, NULL);
+    WriteFile(file, NE_names, sizeof(NE_names), &size, NULL);
+
+    CloseHandle(file);
+}
+
+void test_create_file_imp(const char* name, const char* contents, size_t len)
+{
+    HANDLE file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    winetest_ok(file != INVALID_HANDLE_VALUE, "can't create file\n");
+    if (file != INVALID_HANDLE_VALUE)
+    {
+        if (contents && len)
+        {
+            DWORD size;
+            WriteFile(file, contents, len, &size, NULL);
+        }
+        CloseHandle(file);
+    }
+}
+